Understanding SpringBoot @Conditional Annotations: A Deep Dive into Conditional Configuration
This article explains how SpringBoot uses the @Conditional family of annotations to control bean creation based on classpath presence, existing beans, configuration properties, resources, and application type, providing practical code examples, common pitfalls, custom condition creation, and interview questions.
Core principle of @Conditional
The root annotation @Conditional determines whether a @Configuration class or @Bean method is created and managed by the Spring container. The bean is loaded only when the specified condition returns true; otherwise it is skipped.
What is @Conditional?
@Conditional was introduced in Spring 4.0 as a conditional bean registration annotation. Its effect is a simple switch: the annotated class or method is instantiated only when the condition is satisfied.
Implementation details
@Conditional relies on Spring's Condition interface, which defines a single method matches() returning a boolean.
Return true: condition satisfied, bean loads.
Return false: condition not satisfied, bean does not load.
During bean loading, Spring parses @Conditional, invokes the corresponding Condition implementation, and decides whether to continue loading the bean.
Spring Boot does not invent @Conditional; it builds a suite of @ConditionalOnXxx annotations that cover common scenarios, so developers rarely need to implement Condition themselves.
Core value
On‑demand loading saves resources (no unnecessary beans, reduced memory and startup time).
Multi‑environment adaptation: a single codebase can support dev, test, prod without separate configurations.
Supports auto‑configuration: Spring Boot loads configuration classes from META-INF/spring.factories and filters them with conditional annotations.
Flexible extension: can implement switch‑style features such as optional SMS, email, cache, etc.
Common @Conditional‑On… annotations
@ConditionalOnClass
Loads when the specified class(es) exist on the classpath.
Implemented by OnClassCondition, which checks the classpath for the given class.
// Only when RedisTemplate.class is on the classpath (i.e., redis starter is added)
@ConditionalOnClass(RedisTemplate.class)
@Configuration
public class RedisAutoConfiguration {
// configure RedisTemplate etc.
}Do not use this annotation to check for your own classes; its purpose is to verify that a dependency is present.
@ConditionalOnMissingClass
Loads when the specified class(es) are absent, useful for compatibility or fallback scenarios.
// Load this configuration only when OldRedisTemplate.class is missing
@ConditionalOnMissingClass("com.example.OldRedisTemplate")
@Configuration
public class NewRedisConfig {
// load new version config...
}@ConditionalOnBean
Loads when the specified bean(s) already exist in the container.
Implemented by OnBeanCondition, which searches the container for the bean type or name.
@Configuration
public class JdbcConfig {
// Only when a DataSource bean exists, create JdbcTemplate
@ConditionalOnBean(DataSource.class)
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}Bean loading order matters. If the referenced bean has not yet been defined, the condition fails. Solutions: reorder definitions, use @DependsOn, or switch to @ConditionalOnClass.
@ConditionalOnMissingBean
Loads when the specified bean(s) are absent. This is the core of Spring Boot’s “convention over configuration”.
@Configuration
public class RedisAutoConfiguration {
// Only when no RedisTemplate bean exists, configure the default one
@ConditionalOnMissingBean
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// default serialization...
return template;
}
}Advanced usage with the name attribute targets a specific bean name:
// Load only when a bean named "myRedisTemplate" is missing
@ConditionalOnMissingBean(name = "myRedisTemplate")
@Bean
public RedisTemplate<String, Object> redisTemplate() {
// configure...
}If multiple beans of the same type exist, the condition is considered satisfied; specify name to target a particular bean.
@ConditionalOnProperty
Evaluates a property from application.yml or application.properties and loads when the property matches the expected value. prefix – property prefix (e.g., spring.redis). name – property name(s) (can be multiple). havingValue – required value. matchIfMissing – whether the condition matches when the property is absent (default false).
@ConditionalOnProperty(
prefix = "sms",
name = "enable",
havingValue = "true",
matchIfMissing = false
)
@Configuration
public class SmsConfig {
@Bean
public SmsService smsService() {
return new AliyunSmsService();
}
}Corresponding application.yml:
sms:
enable: true # set false to disableMultiple properties can be required:
@ConditionalOnProperty(
prefix = "redis",
name = {"enable", "cluster.enable"},
havingValue = "true"
)
// Loads only when both redis.enable=true and redis.cluster.enable=true@ConditionalOnResource
Loads when a specific resource file exists on the classpath (e.g., XML, properties, txt).
// Load MyBatis configuration only if mybatis-config.xml exists
@ConditionalOnResource(resources = "classpath:mybatis-config.xml")
@Configuration
public class MyBatisConfig {
// MyBatis configuration logic...
}@ConditionalOnWebApplication
Loads only when the current application is a web application (Servlet or Reactive).
Spring checks for web‑related beans such as ServletContext or DispatcherServlet.
// Register interceptor only in a web application
@ConditionalOnWebApplication
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// register interceptors...
}
}Other common annotations
@ConditionalOnNotWebApplication – opposite of @ConditionalOnWebApplication.
@ConditionalOnSingleCandidate – loads when there is exactly one bean of the specified type.
@ConditionalOnJava – loads when the JVM version matches (e.g., @ConditionalOnJava(JavaVersion.EIGHT)).
@ConditionalOnExpression – evaluates a SpEL expression for complex conditions.
Combining multiple condition annotations
Multiple condition annotations are logically ANDed; all must be satisfied for the configuration class or bean to be loaded.
Real‑world example: custom Redis configuration with four conditions
Running in a web application.
Redis dependency present ( RedisTemplate class on classpath).
Configuration property spring.redis.enable=true.
No custom RedisTemplate bean already defined.
@Configuration
@ConditionalOnWebApplication // 1
@ConditionalOnClass(RedisTemplate.class) // 2
@ConditionalOnProperty(
prefix = "spring.redis",
name = "enable",
havingValue = "true"
) // 3
@ConditionalOnMissingBean(RedisTemplate.class) // 4
public class CustomRedisConfig {
@Bean
public RedisTemplate<String, Object> customRedisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// custom JSON serializer to avoid garbled characters
GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer();
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(jsonSerializer);
template.afterPropertiesSet();
return template;
}
}Spring evaluates the annotations in declaration order and stops as soon as a condition fails, improving efficiency.
Custom condition annotations
When built‑in annotations are insufficient, you can create your own by implementing Condition and defining a meta‑annotation.
Example: load different beans based on operating system
// Windows condition
public class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String osName = context.getEnvironment().getProperty("os.name");
return osName != null && osName.toLowerCase().contains("windows");
}
}
// Linux condition
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String osName = context.getEnvironment().getProperty("os.name");
return osName != null && osName.toLowerCase().contains("linux");
}
} @Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Conditional(WindowsCondition.class)
public @interface ConditionalOnWindows {}
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Conditional(LinuxCondition.class)
public @interface ConditionalOnLinux {} @Configuration
public class FileConfig {
// Windows bean
@ConditionalOnWindows
@Bean
public FileService windowsFileService() {
return new WindowsFileService();
}
// Linux bean
@ConditionalOnLinux
@Bean
public FileService linuxFileService() {
return new LinuxFileService();
}
}Verification: on Windows the container contains windowsFileService, on Linux it contains linuxFileService, and on macOS neither bean is loaded.
Pitfalls to avoid
Using @ConditionalOnBean for a bean that has not yet been loaded leads to failure. Fix by reordering, using @DependsOn, or switching to @ConditionalOnClass.
@ConditionalOnClass may appear satisfied but still fail if the dependency is not activated; ensure the dependency is correctly declared and enabled.
@ConditionalOnMissingBean without a name can cause conflicts when multiple beans of the same type exist; specify the name attribute to target a particular bean.
Incorrect matchIfMissing setting for @ConditionalOnProperty can unintentionally disable a feature; set matchIfMissing=true when you want the condition to pass by default.
Place cheap, fast‑checking conditions (e.g., @ConditionalOnWebApplication, @ConditionalOnClass) before expensive ones (e.g., @ConditionalOnResource) to improve startup performance.
Interview questions
Core principle of Spring Boot auto‑configuration
Spring Boot reads META-INF/spring.factories to load auto‑configuration classes and then filters them with @Conditional annotations, loading only the configurations that match the current environment.
Difference between @Conditional and @ConditionalOnBean
@Conditional is the base annotation requiring a custom Condition implementation; @ConditionalOnBean is a ready‑made Spring Boot extension that checks for the presence of a bean.
Why does a custom RedisTemplate override the default one?
The default RedisTemplate is guarded by @ConditionalOnMissingBean. When a user‑defined RedisTemplate bean exists, the condition is not met, so the auto‑configuration is skipped.
How to create a custom condition annotation?
Implement Condition, override matches(), then create a meta‑annotation annotated with @Conditional linking to the Condition class.
Summary
Root annotation @Conditional drives conditional bean registration via Condition.matches().
Key built‑in annotations: @ConditionalOnClass, @ConditionalOnMissingBean, @ConditionalOnBean, @ConditionalOnProperty, @ConditionalOnWebApplication.
Multiple annotations are combined with AND logic; order them from simple to complex for efficiency.
They enable on‑demand loading, multi‑environment adaptation, and are essential for Spring Boot auto‑configuration.
Avoid common pitfalls: bean loading order, matchIfMissing misuse, and incorrect classpath checks.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Tech Workshop
Focused on Java backend technologies, sharing fundamentals, multithreading, JVM, the Spring ecosystem, microservices, distributed systems, high concurrency, source‑code analysis, and practical experience. Continuously delivers high‑quality original content, interview guides, and learning roadmaps to help Java developers progress from beginner to advanced, enhancing technical skills and core competitiveness.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
