skip to content

Debugging condition evaluation

Running with debug enabled prints the condition evaluation report — positive matches, negative matches and exclusions — so you can see exactly why a bean did or did not appear. Interviewers ask because it is the first move when Boot 'ignores' your configuration.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

questions

5

A bean you expected from auto-configuration isn't in the context. How do you use the condition report to find out why, and what are the usual causes?

level: seniorimportance: must knowfreq 50%

answer

  1. --debug → find class in Negative matches
  2. OnClass = missing dependency
  3. OnMissingBean = you already defined it
  4. OnProperty = flag unset/wrong value
  5. OnMissingBean ordering-sensitive → duplicate beans

basics

~20 s

Run with --debug and find the auto-config under Negative matches; the reason line names the failing condition. Usual causes: a required class is missing from the classpath (@ConditionalOnClass), a property isn't set (@ConditionalOnProperty), or your own bean already exists so auto-config backed off (@ConditionalOnMissingBean).

solid answer

~40 s

Enable --debug and search the CONDITIONS EVALUATION REPORT for the auto-config class. Under Negative matches the report states the exact condition that failed. The common culprits: (1) @ConditionalOnClass — the starter/dependency isn't on the classpath, so add it; (2) @ConditionalOnProperty — a gating property is unset or matches the wrong value; (3) @ConditionalOnMissingBean — you (or another config) already defined a bean of that type, so auto-config deliberately backed off; (4) @ConditionalOnBean — a prerequisite bean doesn't exist yet; (5) the class is under Exclusions. A subtle one: @ConditionalOnMissingBean is evaluated in configuration-processing order, so a user bean defined 'after' the auto-config may not be seen, causing a duplicate. If the config is a Positive match but the bean's still absent, check the @Bean-method-level conditions and active profiles.

code

java · 22 lines
java
// Symptom: no RedisTemplate bean. Report shows:
//
// Negative matches:
//   RedisAutoConfiguration:
//     Did not match:
//       - @ConditionalOnClass did not find required class
//         'org.springframework.data.redis.core.RedisOperations' (OnClassCondition)
//
// Cause + fix: the Redis starter is missing.
//   implementation("org.springframework.boot:spring-boot-starter-data-redis")

// Symptom: your custom ObjectMapper is ignored / duplicated.
// Define the override in a plain @Configuration so it's seen early:
@Configuration(proxyBeanMethods = false)
class JacksonOverrideConfig {
    @Bean
    ObjectMapper objectMapper() {           // seen before JacksonAutoConfiguration
        return new ObjectMapper().findAndRegisterModules();
    }
}
// Now JacksonAutoConfiguration#jacksonObjectMapper appears under
// Negative matches: @ConditionalOnMissingBean found beans 'objectMapper'.

go deeper

for a junior

Enable --debug, find the class in Negative matches, read the reason. Know OnClass = missing dependency.

for a middle

Enumerate the common failing conditions and their concrete fixes.

for a senior

Explain the OnMissingBean ordering gotcha and Positive-but-still-missing (nested @Bean conditions).

for a principal

Connect to auto-config ordering (@AutoConfigureBefore/After), bean-override policy, and correct override placement patterns.

## Systematic procedure 1. **Reproduce** with `--debug` (or hit `/actuator/conditions`). 2. **Locate** the auto-config by name (e.g. `RedisAutoConfiguration`, `DataSourceAutoConfiguration`). 3. **Classify** where it appears: - **Negative matches** → read the failing condition (the fix is right there). - **Exclusions** → it was excluded on purpose (`spring.autoconfigure.exclude` / `exclude=`). Remove the exclusion. - **Positive matches** but bean still missing → the class ran; inspect nested `@Bean` conditions, profiles, and property values. - **Absent entirely** → the auto-config isn't registered at all (wrong/missing starter, or not listed in `META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports`). ## The usual failing conditions and their fixes - **`@ConditionalOnClass did not find required class '...'`** → the dependency/starter is missing. Add it. This is the single most common cause. - **`@ConditionalOnMissingBean ... found beans '...'`** → auto-config's whole point is 'give you one only if you didn't'. You already declared that bean type, so it stepped aside. If you wanted the auto one, remove/rename yours; if you wanted yours, this is working as intended. - **`@ConditionalOnProperty (x.y) did not find property'** or matched wrong value** → set/correct the property. Watch `havingValue` and `matchIfMissing`. - **`@ConditionalOnBean` did not find bean** → an upstream bean this one depends on wasn't created (often a chain — fix the root cause first). - **`@ConditionalOnMissingClass found unwanted class`** → something on the classpath vetoes it (e.g. presence of an alternative implementation). - **`@ConditionalOnWebApplication` / `@ConditionalOnNotWebApplication`** → wrong application type (servlet vs reactive vs none). ## The ordering gotcha (senior insight) `@ConditionalOnMissingBean` only sees beans that have been **registered so far** when the condition is evaluated. Auto-configs are ordered (via `@AutoConfiguration(before/after)` / `@AutoConfigureBefore`/`@AutoConfigureAfter`), and user `@Configuration` is generally processed before auto-config. But if your bean is contributed by a component scanned/registered *after* the auto-config's condition runs, the condition may not see it → you get **two** beans or an unexpected auto bean. The lesson: define overriding beans in a plain user `@Configuration` (processed early), not somewhere that races the auto-config. This ordering subtlety is exactly what the report helps you catch: you'll see the auto-config as a Positive match ('did not find any beans') even though your bean also exists. ## Reading the message text Messages originate from `ConditionOutcome.getMessage()` produced by conditions like `OnClassCondition`, `OnBeanCondition`, `OnPropertyCondition`. They're descriptive and stable enough to act on, though not a formal contract to parse in code. ## Related: bean overriding Even if a bean exists, `spring.main.allow-bean-definition-overriding` (default false since Boot 2.1) affects duplicate-definition behavior — a duplicate now throws rather than silently overriding. That's a different failure mode but often co-occurs with condition confusion. ## Summary heuristic 'Expected bean missing' → Negative match → 90% of the time it's OnClass (missing dep), OnProperty (unset flag), or OnMissingBean (you defined it). The report tells you which in one line.

  • Why might you end up with two beans of a type even though the auto-config uses @ConditionalOnMissingBean?
    Because @ConditionalOnMissingBean only sees beans registered before it is evaluated. If your bean is contributed after the auto-config's condition runs (bad ordering), the condition finds nothing and creates its own — you get two. Define overrides in early-processed user @Configuration.
  • The report shows the auto-config under Positive matches but the bean still isn't there. What now?
    The class ran, so the class-level conditions passed. Check the individual @Bean method conditions (a nested @ConditionalOnProperty/OnMissingBean can veto just that bean), active profiles, and the actual property values feeding it.

saying these in an interview costs you the question

  • Assuming a missing bean always means a missing dependency (could be OnMissingBean backing off because you defined it)
  • Not knowing @ConditionalOnMissingBean is ordering-sensitive
  • Trying to 'force' the bean by excluding random auto-configs instead of reading the reason line

context

open as a page

How do you make Spring Boot print the auto-configuration condition evaluation report, and what is it for?

level: juniorimportance: should knowfreq 55%

basics

~20 s

Start the app with the --debug flag (or set debug=true in application.properties). Spring Boot then logs a report showing which auto-configurations were applied and which were skipped, helping you understand why a bean did or didn't get created.

open as a page

Walk me through the sections of the condition evaluation report and what each tells you.

level: middleimportance: should knowfreq 45%

basics

~20 s

The report has four parts: Positive matches (auto-configs that were applied and why), Negative matches (ones skipped and the condition that failed), Exclusions (explicitly excluded configs), and Unconditional classes (configs with no conditions that always load).

open as a page

How can you access the ConditionEvaluationReport programmatically, e.g. in a test, instead of reading logs?

level: seniorimportance: should knowfreq 30%

basics

~10 s

The report is itself a bean. Call ConditionEvaluationReport.get(beanFactory) to obtain it, then inspect getConditionAndOutcomesBySource(). In tests, ApplicationContextRunner exposes it via assertThat(context).getBean(...) or by inspecting the runner's context so you can assert on matches.

open as a page

Explain how the ConditionEvaluationReport is populated during context startup and why condition ordering affects what the report shows.

level: principalimportance: nice to knowfreq 15%

basics

~20 s

As Spring processes configuration classes, each @Conditional is evaluated by a ConditionEvaluator; the outcome is recorded into the ConditionEvaluationReport keyed by source. Because bean conditions (@ConditionalOnBean/OnMissingBean) only see beans registered so far, the order configs are processed changes the outcomes — and thus what the report records.

open as a page