skip to content

What is additional-spring-configuration-metadata.json and when do you need it?

level: middleimportance: should knowfreq 40%

answer

  1. src/main/resources/META-INF/additional-spring-configuration-metadata.json
  2. Hand-written, merged into generated file
  3. For @Value/Environment-only keys and value hints
  4. Same schema: groups/properties/hints
  5. Manual entry wins on conflict

basics

~10 s

It 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 s

The 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
json
{
  "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

for a junior

Know it is a hand-written file for metadata the processor cannot infer.

for a middle

Explain the path, the merge behavior, the schema, and typical uses like value hints and @Value-only keys.

for a senior

Discuss precedence rules, providers, and keeping it in sync with code as a maintenance concern.

for a principal

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

context