Auto-Configuration: @ConditionalOnClass, @ConditionalOnMissingBean
- Last updated
- Prerequisites:
- Spring configuration and beans basics
- Understanding of @Configuration and @Bean
- spring
- spring-boot
- auto-configuration
- conditional
- ConditionalOnClass
- ConditionalOnMissingBean
Read at your depth
The practical view
Spring Boot auto-configuration creates beans automatically based on what's on the classpath and your properties. It's driven by @Conditional annotations: @ConditionalOnClass(name = "org.h2.Driver") creates the bean only if that class is present; @ConditionalOnMissingBean(DataSource.class) only if you haven't defined your own; @ConditionalOnProperty, @ConditionalOnExpression, @ConditionalOnWebApplication. Your own @Configuration classes are processed AFTER auto-configuration, so a bean you define with the same type overrides the auto-configured one (via @ConditionalOnMissingBean).
The same idea in other frameworks
N/A — server-side Spring Boot concern.N/A — Spring concern.N/A — Spring concern.N/A — Spring concern.Legacy vs modern
Hand-wiring beans vs auto-configuration with conditionals
Manually configuring beans for every dependency is boilerplate; Boot's conditional auto-configuration creates sensible defaults that back off when you override.
@Bean
DataSource dataSource() {
return new DriverManagerDataSource("jdbc:h2:mem:test", "sa", ""); // ❌ OLD: hand-wired
}// spring-boot-starter-jdbc on the classpath + application.properties
// spring.datasource.url=... → ✅ NEW: DataSource auto-configuredInterview gotchas
Context
This tests the bean-definition timing model — interviewers want to know that the missing-bean check happens during registration, so ordering (user config vs auto-config) determines the outcome.
The mechanical answer
@ConditionalOnMissingBean is evaluated when the auto-configuration class's bean definitions are being registered. It checks whether a bean definition of the specified type already exists at that moment. Because user-defined @Configuration classes are processed BEFORE auto-configuration classes (by design), a user bean is typically visible and the auto-configured bean backs off. However, if a bean is defined in a way that registers later (e.g. another auto-configuration ordered after, a @Bean defined in an @Import processed later, or a bean created from a factory that hasn't registered yet), the condition may not see it, and BOTH beans get created — leading to ambiguity. The fix is to understand the ordering (use @AutoConfigureBefore/After or define the override early) and that the check is registration-time, not injection-time.
Trap
The naive answer is 'if I define a bean, auto-configuration always skips'. The check is timing-sensitive and registration-based; late-registered beans can coexist with the auto-configured one, causing a NoUniqueBeanDefinitionException. Another trap: thinking @ConditionalOnMissingBean checks at runtime — it's evaluated at bean-definition registration during context refresh. Candidates who describe the registration-order model show real understanding.
Context
This probes the condition evaluation model — interviewers want the AND semantics and the typical combination pattern (class present + missing bean + property enabled).
The mechanical answer
Conditions are evaluated with AND semantics: every @Conditional annotation on a class or @Bean method must pass for the bean to be registered. The ConditionEvaluator evaluates all conditions in the order they appear; the first failure skips the registration. The common pattern is a stack: @ConditionalOnClass(name = "com.example.FeatureClient") (the dependency exists), @ConditionalOnProperty(prefix = "app.feature", name = "enabled", havingValue = "true") (the user opted in), and @ConditionalOnMissingBean(FeatureClient.class) (the user didn't define their own). If the class is absent, the property is unset, or a user bean exists, the auto-configured bean is skipped. Each condition reads different sources (classpath metadata, Environment, existing bean definitions), and they compose to make auto-configuration safe and overridable.
Trap
The naive answer is 'conditions are OR — any one passing enables the bean'. They are AND — all must pass. Another trap: assuming @ConditionalOnClass loads the class — it uses name-based metadata checks precisely to avoid loading optional classes (which could fail with NoClassDefFoundError). Candidates who explain the AND evaluation and name-based class checks show real depth.