What is an EnvironmentPostProcessor in Spring Boot and at what point in startup does it run?
answer
- postProcessEnvironment(ConfigurableEnvironment, SpringApplication)
- runs before context + beans
- mutate PropertySources early
- register in spring.factories, not @Component
- no @Autowired / @Value available
basics
~10 sIt is a Spring Boot hook that can add or change configuration properties very early in startup — while the Environment is being prepared, before the application context and any beans are created.
solid answer
~30 sEnvironmentPostProcessor is a Spring Boot SPI (interface org.springframework.boot.env.EnvironmentPostProcessor) with one method, postProcessEnvironment(ConfigurableEnvironment, SpringApplication). Spring Boot invokes it during environment preparation — after application.properties/yaml and command-line args are loaded, but before the ApplicationContext is created and before any beans exist. Its job is to inspect, add, remove, or reorder PropertySources on the Environment: e.g. inject computed defaults, decrypt secrets, or pull config from an external store. Because it runs pre-context, you cannot @Autowired anything into it; it is a plain object Spring Boot instantiates itself. You register it in META-INF/spring.factories, not as a @Component.
code
java · 24 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 java.util.Map;
public class DefaultsEnvironmentPostProcessor implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
// Add a low-precedence property source with computed defaults.
Map<String, Object> defaults = Map.of("app.feature.enabled", "true");
environment.getPropertySources()
.addLast(new MapPropertySource("appDefaults", defaults));
}
}
// META-INF/spring.factories:
// org.springframework.boot.env.EnvironmentPostProcessor=\
// com.example.DefaultsEnvironmentPostProcessorgo deeper
Know it's an early hook to change config before beans exist and that you register it in spring.factories.
Explain the ApplicationEnvironmentPreparedEvent timing and that no DI is available.
Contrast with BeanFactoryPostProcessor/BeanPostProcessor and articulate why the pre-context timing is the whole point.
Reason about precedence effects on conditional auto-config and profile activation caused by early property mutation.
## What it is `EnvironmentPostProcessor` is a Spring Boot **SPI (Service Provider Interface)** — a plug-in contract you implement. The interface lives in `org.springframework.boot.env` and has a single method: ```java void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application); ``` The **Environment** is Spring's abstraction over all configuration — it holds an ordered list of **PropertySources** (application.properties, YAML, OS environment variables, system properties, command-line args, etc.) and answers `getProperty("some.key")`. An `EnvironmentPostProcessor` gets a chance to **mutate that list** before anything else uses it. ## When it runs Spring Boot startup order (simplified): 1. `SpringApplication.run()` starts. 2. Boot **prepares the Environment**: creates it, adds default property sources, binds command-line args, and fires `ApplicationEnvironmentPreparedEvent`. 3. An internal listener, `EnvironmentPostProcessorApplicationListener`, catches that event and runs **all registered `EnvironmentPostProcessor`s**. 4. Only *after* that does Boot create the `ApplicationContext`, register bean definitions, and instantiate beans. So the key mental model: **EPPs run before the context and beans exist.** There is no dependency injection available, no `@Value`, no other beans to call. ## Why the timing matters Because it runs so early, whatever an EPP writes into the Environment is visible to *everything* downstream: `@ConfigurationProperties` binding, `@Value` injection, conditional auto-configuration (`@ConditionalOnProperty`), and profile activation. That is exactly why you'd use it — to shape configuration before Spring reacts to it. ## Typical uses - Add computed or default properties (e.g. derive a URL from other properties). - Decrypt encrypted secret values into their plaintext property source. - Load configuration from a custom external source (a legacy file format, a remote store) as a new `PropertySource`. - Reorder property sources to change precedence. ## How you register it NOT as a `@Component` (the context doesn't exist yet, so component scanning hasn't happened). You list it in `META-INF/spring.factories`: ``` org.springframework.boot.env.EnvironmentPostProcessor=\ com.example.MyEnvironmentPostProcessor ``` ## Common gotcha Candidates often try to `@Autowired` a service or use `@Value` inside an EPP — impossible, because it runs pre-context. Logging is also tricky: the logging system isn't fully initialized yet (covered in a separate question).
- Can you inject another bean into an EnvironmentPostProcessor?No. It runs during environment preparation, before the ApplicationContext and any beans exist, so there is nothing to inject and component scanning hasn't happened.
- What object does postProcessEnvironment give you to modify config?A ConfigurableEnvironment. You call environment.getPropertySources() to get a MutablePropertySources and add/remove/reorder PropertySource entries.
saying these in an interview costs you the question
- Thinking it runs after beans are created or that you can @Autowired into it.
- Registering it with @Component and expecting component scanning to pick it up.
- Confusing it with BeanFactoryPostProcessor or BeanPostProcessor, which operate on bean definitions/beans much later.