skip to content

How does @ConditionalOnMissingBean let a user override an auto-configured bean? What ordering rules make this work reliably?

level: seniorimportance: must knowfreq 66%

answer

  1. default = last resort, backs off
  2. user @Configuration processed BEFORE auto-config
  3. sees only already-processed definitions -> order sensitive
  4. matches on return type by default
  5. docs: only use on auto-config classes

basics

~10 s

Auto-config beans are marked @ConditionalOnMissingBean, so they register only if the user hasn't defined one of that type. Because user @Configuration is processed before auto-configuration, a user bean wins and the default backs off.

solid answer

~40 s

@ConditionalOnMissingBean registers the annotated bean only when no bean of that type (or matching name) already exists. Boot uses it pervasively so its defaults are 'last resort': DataSource, ObjectMapper, RestTemplateBuilder, etc. all back off if you declare your own. The mechanism depends on ordering — auto-configuration classes are imported via @AutoConfiguration/@ImportAutoConfiguration and are processed AFTER your regular @Configuration, and they're internally ordered with @AutoConfigureBefore/After/@AutoConfigureOrder. When the condition evaluates, your bean definition already exists, so OnMissingBean sees it and skips the default. Crucial gotcha: OnMissingBean only sees bean definitions that have already been processed — it is order-sensitive and works reliably for auto-config-vs-user code but is fragile between two beans in the same @Configuration or across auto-configs without explicit ordering.

code

java · 19 lines
java
// --- Library auto-configuration (in a starter) ---
@AutoConfiguration
public class GreetingAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean          // default only if the app didn't define one
    GreetingService greetingService() {
        return new DefaultGreetingService();
    }
}

// --- User application overrides it simply by declaring their own ---
@Configuration(proxyBeanMethods = false)
class MyConfig {
    @Bean
    GreetingService greetingService() {   // processed BEFORE auto-config
        return new CustomGreetingService(); // -> OnMissingBean backs off
    }
}

go deeper

for a junior

Know it means 'register only if the user didn't provide one', enabling overrides.

for a middle

Explain default matching on return type and the back-off concept.

for a senior

Explain the ordering dependency (user config first, auto-config last) and its fragility.

for a principal

Discuss @AutoConfigureBefore/After ordering, why the docs restrict it to auto-config, and using ConditionEvaluationReport to debug.

## The override contract `@ConditionalOnMissingBean` matches when **no bean matching the given criteria already exists** in the `BeanFactory`. Spring Boot puts it on almost every default bean so users can override by simply declaring their own — the default 'backs off'. This is the single most important reason Boot feels customizable without config files. ### Matching criteria By default it matches on the **return type** of the `@Bean` method. You can narrow/broaden it: - `value` / `type` — match by type(s). - `name` — match by bean name. - `annotation` — match beans carrying an annotation. - `ignored` / `ignoredType` — exclude certain types from the check. - `parameterizedContainer` — consider generic containers. ```java @Bean @ConditionalOnMissingBean // matches on ObjectMapper (the return type) ObjectMapper objectMapper() { ... } ``` ## Why ordering is everything `@ConditionalOnMissingBean` inspects only bean definitions **already known** when the condition runs. So the override works **because of processing order**: 1. **User `@Configuration` / component-scanned beans are processed first.** Regular configuration is registered before auto-configuration. 2. **Auto-configuration runs last.** Classes listed in `META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports` are imported via `@EnableAutoConfiguration`/`AutoConfigurationImportSelector` and deliberately ordered to come after user config. 3. When `DataSourceAutoConfiguration`'s conditional bean evaluates, a user-declared `DataSource` (from a regular `@Configuration`) is already present ⇒ `OnMissingBean` fails ⇒ the default is skipped. ### Ordering *within* auto-configuration Across auto-config classes, order is controlled by `@AutoConfigureBefore`, `@AutoConfigureAfter`, and `@AutoConfigureOrder`. This matters when one auto-config's `OnMissingBean` must see beans from another. Getting this wrong is a classic source of 'sometimes my bean, sometimes the default' bugs. ## The order-sensitivity gotcha Because it's evaluated against **already-processed** definitions, `@ConditionalOnMissingBean`: - **Works** for auto-config-vs-user code (guaranteed order). - **Is fragile** between two `@Bean` methods in the **same** `@Configuration` (method processing order isn't a contract you should lean on). - **Is fragile** across two auto-configs without explicit `@AutoConfigureBefore/After`. - Reference docs explicitly warn: **use `@ConditionalOnMissingBean` / `@ConditionalOnBean` only on auto-configuration classes**, because they load in a well-defined order; in user config the order is far less predictable. ## Interaction with proxyBeanMethods Boot's auto-configs use `@Configuration(proxyBeanMethods = false)` for speed; this doesn't change OnMissingBean semantics but avoids CGLIB proxying of config methods. ## Diagnosing back-off Run with `--debug` (or `logging.level` for the report) to get the **ConditionEvaluationReport** ('Positive/Negative matches'), which shows exactly which `OnMissingBean` matched or backed off and why — the primary tool when an override doesn't take. ## When to use Use it in **your own starters/auto-config** so consumers can override your defaults. In plain application `@Configuration`, prefer just defining the bean you want, or use `@Primary`/explicit naming rather than relying on OnMissingBean ordering.

  • Why does Spring recommend using @ConditionalOnMissingBean only inside auto-configuration classes and not ordinary @Configuration?
    Because it only sees bean definitions already processed when it evaluates. Auto-config classes load in a well-defined order (after user config, ordered by @AutoConfigureBefore/After), so the check is deterministic. In ordinary @Configuration the processing order of bean methods isn't a reliable contract, so the condition can match inconsistently.
  • Your custom bean sometimes gets overridden by the default. How do you diagnose it?
    Enable the ConditionEvaluationReport (run with --debug or raise the autoconfigure logging level) to see positive/negative matches. Then check auto-config ordering (@AutoConfigureBefore/After) and whether your bean is declared in regular @Configuration so it's registered before the auto-config evaluates.

saying these in an interview costs you the question

  • Claiming OnMissingBean scans the entire final context regardless of order
  • Saying user beans are processed after auto-configuration
  • Recommending OnMissingBean between two @Bean methods in the same user @Configuration as if order were guaranteed
  • Confusing it with @Primary (which resolves ambiguity, not registration)

context