What is additional-spring-configuration-metadata.json and when do you need it?
answer
- src/main/resources/META-INF/additional-spring-configuration-metadata.json
- Hand-written, merged into generated file
- For @Value/Environment-only keys and value hints
- Same schema: groups/properties/hints
- Manual entry wins on conflict
basics
~10 sIt is a hand-written file in src/main/resources/META-INF/ where you add metadata the processor cannot infer — like value hints, descriptions, or deprecations. At compile time the processor merges it into the generated spring-configuration-metadata.json.
solid answer
~40 sThe processor can only describe what it can statically see: @ConfigurationProperties classes with getters or constructor binding. It cannot know about properties you read directly via Environment/@Value, nor can it guess the legal values of a String property. For those cases you author META-INF/additional-spring-configuration-metadata.json by hand. It uses the same schema (groups, properties, hints) and the processor merges its contents into the final spring-configuration-metadata.json during compilation. Typical uses: adding value hints so the IDE suggests an enum-like set of allowed strings; adding a description for a property the processor found but you want to enrich; declaring a deprecation with a reason and replacement; or fully declaring a property that has no backing @ConfigurationProperties field at all. Manual entries take precedence over generated ones for the same property name.
code
json · 20 lines{
"properties": [
{
"name": "app.feature.strategy",
"type": "java.lang.String",
"description": "Rollout strategy read via @Value, so not auto-detected.",
"defaultValue": "gradual"
}
],
"hints": [
{
"name": "app.feature.strategy",
"values": [
{ "value": "off", "description": "Feature disabled." },
{ "value": "gradual", "description": "Percentage-based rollout." },
{ "value": "on", "description": "Fully enabled." }
]
}
]
}go deeper
Know it is a hand-written file for metadata the processor cannot infer.
Explain the path, the merge behavior, the schema, and typical uses like value hints and @Value-only keys.
Discuss precedence rules, providers, and keeping it in sync with code as a maintenance concern.
Weigh it as a DX investment; standardize its use across modules and treat hints/deprecations as part of the config contract.
## Why it exists The annotation processor is a **static** tool — it only knows what it can read from your source at compile time. Several important things fall outside that: 1. **Properties without a bindable field** — keys you read via `environment.getProperty(...)` or `@Value("${...}")` never appear, because there is no `@ConfigurationProperties` accessor to detect. 2. **Legal value sets** — for a `String logLevel`, the processor knows the type is `String` but has no idea the sensible values are `debug/info/warn/error`. 3. **Richer docs / deprecations** — you may want to override or add a description, or flag a property as deprecated with a replacement. For all of these you write metadata **by hand**. ## Where it lives and how it merges Put the file at: ``` src/main/resources/META-INF/additional-spring-configuration-metadata.json ``` During compilation, `spring-boot-configuration-processor` **reads this file and merges it** into the generated `META-INF/spring-configuration-metadata.json`. You never edit the generated file directly (it is regenerated on every build); you edit the *additional* file. ## Schema It uses the exact same JSON structure as the generated file — top-level arrays `groups`, `properties`, and `hints`: ```json { "properties": [ { "name": "app.log-level", "type": "java.lang.String", "description": "Application log level.", "defaultValue": "info" } ], "hints": [ { "name": "app.log-level", "values": [ { "value": "debug", "description": "Verbose diagnostic output." }, { "value": "info", "description": "Normal output." }, { "value": "warn" }, { "value": "error" } ] } ] } ``` The `hints` array is the mechanism that drives **value auto-completion**: `name` links a hint to a property (or to `<prop>.keys` / `<prop>.values` for maps), and `values` lists suggested values with optional descriptions. ## Precedence If a property appears both in generated metadata and in the additional file, the **manual entry wins** for the fields it specifies. This lets you enrich or correct generated descriptions. ## Common uses - **Value hints** for enum-like strings (as above). - **Providers** (e.g. `handle-as`, `class-reference`, `logger-name`) for dynamic suggestions — a separate deeper topic. - **Deprecations** — mark a property deprecated with `level`, `reason`, `replacement`. - **Declaring otherwise-invisible properties** read only via `Environment`/`@Value`. ## Gotchas - The file must be under `src/main/resources/META-INF/` with the **exact** name; a typo means it is silently ignored. - It is JSON, not YAML — trailing commas or comments break it. - Changes require a **rebuild** for the IDE to pick them up. - It complements, not replaces, the generated file — you still keep the processor dependency. ## When to use Reach for it whenever the IDE experience is missing something the processor cannot infer: value suggestions, docs for `@Value`-only keys, or deprecation notices.
- Why can't the processor generate value hints automatically for a String property?Because the legal values are a domain concept not encoded in the type. The processor only sees java.lang.String. It can auto-suggest values for enums and booleans (finite known sets), but for arbitrary strings you must supply hints manually.
- If the same property appears in both the generated and additional files, which wins?The manual additional-spring-configuration-metadata.json entry takes precedence for the fields it defines, letting you override or enrich generated descriptions and defaults.
saying these in an interview costs you the question
- Editing the generated spring-configuration-metadata.json directly (it is overwritten each build)
- Putting the file in the wrong path or misspelling it
- Thinking it is YAML or supports comments
- Believing you no longer need the processor once you have the additional file