Inside postProcessEnvironment, how do you add a PropertySource and control whether it overrides or defers to existing configuration?
answer
- first = wins, last = fallback
- MutablePropertySources: addFirst/addLast/addBefore/addAfter
- MapPropertySource / PropertiesPropertySource, unique name
- addFirst also beats command-line args
- addBefore/addAfter throw if named source missing
basics
~10 sGet environment.getPropertySources() (a MutablePropertySources) and call addFirst to make your source win over others, or addLast to act only as a fallback. You can also use addBefore/addAfter relative to a named source.
solid answer
~40 sThe Environment resolves a property by walking its ordered PropertySources front-to-back and returning the first match, so position = precedence. In postProcessEnvironment you call environment.getPropertySources(), which returns a MutablePropertySources, then: addFirst(source) to give highest precedence (overrides application.properties, even command-line if placed ahead), addLast(source) to make it a low-precedence default that anything else can override, or addBefore("name", source)/addAfter("name", source) to slot it relative to a known source. Wrap your data in a concrete PropertySource such as MapPropertySource or PropertiesPropertySource, each needing a unique name. Choosing addFirst vs addLast is the key design decision: defaults go last, overrides/decrypted secrets typically go first (or just before the source they replace).
code
java · 32 linespackage com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.MutablePropertySources;
import java.util.Map;
public class PrecedenceEnvironmentPostProcessor implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
MutablePropertySources sources = environment.getPropertySources();
// Overridable defaults -> lowest precedence.
sources.addLast(new MapPropertySource("appDefaults",
Map.of("app.timeout", "30s")));
// A value that must win over file config -> highest precedence.
sources.addFirst(new MapPropertySource("forcedOverrides",
Map.of("app.region", "eu-central-1")));
// Slot relative to a known source (guard existence first).
if (sources.contains("systemEnvironment")) {
sources.addAfter("systemEnvironment", new MapPropertySource("afterEnv",
Map.of("app.fallback", "true")));
}
}
}go deeper
Know addFirst wins and addLast is a fallback, wrapped in a MapPropertySource.
Explain the front-to-back resolution model and addBefore/addAfter with existence guards.
Reason about overriding file config without beating command-line args and ordering vs ConfigDataEnvironmentPostProcessor.
Design a precedence strategy for a library EPP that is predictable regardless of app-side property sources and EPP ordering.
## Precedence model you must understand first A Spring `Environment` holds a **`MutablePropertySources`** — an ordered collection of `PropertySource` objects. When you ask `getProperty("x")`, it iterates from **first to last** and returns the value from the **first** source that contains the key. Therefore **earlier = higher precedence** (it wins). So the entire question of "does my source override or defer?" is answered by **where you insert it**. ## The MutablePropertySources API `environment.getPropertySources()` returns `MutablePropertySources`, with: - `addFirst(PropertySource)` — highest precedence; wins over everything already there. - `addLast(PropertySource)` — lowest precedence; a fallback/default. - `addBefore(String relativeName, PropertySource)` — insert just ahead of a named source. - `addAfter(String relativeName, PropertySource)` — insert just behind a named source. - `replace(String name, PropertySource)`, `remove(String name)` — swap or drop by name. `addBefore`/`addAfter` throw if the named source doesn't exist, so guard with `contains(name)`. ## Concrete PropertySource types You wrap your data in an implementation: - `MapPropertySource(name, Map<String,Object>)` — for a Map. - `PropertiesPropertySource(name, Properties)` — for a `java.util.Properties`. - `SystemEnvironmentPropertySource` — special relaxed-binding source for env vars. Each source needs a **unique name**; two sources with the same name cause an exception on add. ## Design decision: addFirst vs addLast - **Defaults you want to be overridable → addLast.** Users can still override in application.properties or env vars. - **Values that must win (decrypted secrets, forced overrides) → addFirst**, or `addBefore` the exact source you intend to override, to avoid accidentally beating command-line arguments. ## Gotchas 1. **Command-line args are near the front.** If you `addFirst`, you also override command-line args — sometimes undesirable. Prefer `addBefore("applicationConfig: [classpath:/application.properties]", ...)` or after the command-line source when you only mean to beat file config. 2. **Boot's config-data sources (`application.properties`/yaml) are added by `ConfigDataEnvironmentPostProcessor`.** If your EPP runs *before* it, those named sources may not exist yet for `addBefore`/`addAfter` — ordering between EPPs matters (implement `Ordered`). 3. **Names must be unique and stable** — other EPPs or tooling may look you up by name. 4. **Relaxed binding / profiles** still apply downstream; your added source participates like any other.
- You added a default with addLast but it's overriding application.properties instead of deferring to it. What went wrong?Almost certainly you used addFirst, or the applicationConfig source was added by a later-running EPP so at your point your source was effectively earlier. Verify ordering and use addLast plus Ordered to run after ConfigDataEnvironmentPostProcessor if you need to sit behind the file sources.
- How do you make your source override application.properties but NOT command-line arguments?Don't blindly addFirst. Use addBefore targeting the applicationConfig property source name, or addAfter the command-line source, so your source sits between them in precedence.
saying these in an interview costs you the question
- Believing addLast gives highest precedence (it's the opposite).
- Assuming property sources are searched last-to-first.
- Reusing an existing source name and expecting a merge rather than an exception.