Understanding Spring @Conditional and Its Derived Annotations
This article explains how Spring Boot’s @Conditional annotation works, shows how to implement custom Condition classes for language switching, and reviews the suite of derived annotations such as @ConditionalOnBean, @ConditionalOnMissingBean, @ConditionalOnClass, @ConditionalOnMissingClass, and @ConditionalOnProperty with concrete code examples.
1. @Conditional
The @Conditional annotation, added in Spring 4, registers a bean only when one or more Condition implementations evaluate to true. Its definition contains a single attribute Class<? extends Condition>[] value() and can be placed on classes or methods.
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
/**
* All {@link Condition} classes that must {@link Condition#matches match}
* in order for the component to be registered.
*/
Class<? extends Condition>[] value();
}The Condition interface requires a
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata)method; returning true registers the bean, false skips it. The interface is marked @FunctionalInterface, so a lambda can be used.
Example: a multilingual scenario where two custom conditions decide which Language bean to load.
@Data
@Builder
public class Language {
private Long id;
private String content;
} public class ChineseCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment env = context.getEnvironment();
String prop = env.getProperty("lang");
return Objects.equals(prop, "zh_CN");
}
} public class EnglishCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment env = context.getEnvironment();
String prop = env.getProperty("lang");
return Objects.equals(prop, "en_US");
}
} @Configuration
public class MyConfig {
@Bean
@Conditional(ChineseCondition.class)
public Language chinese() {
return Language.builder().id(1L).content("华流才是最屌的").build();
}
@Bean
@Conditional(EnglishCondition.class)
public Language english() {
return Language.builder().id(2L).content("english is good").build();
}
public static void main(String[] args) {
System.setProperty("lang", "zh_CN");
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MyConfig.class);
for (String name : ctx.getBeanDefinitionNames()) {
System.out.println(name);
}
}
}Running the program prints chinese, confirming that ChineseCondition returned true and the corresponding bean was injected.
2. Derived @Conditional annotations
2.1 @ConditionalOnBean
Creates a bean only when a specified bean already exists in the context.
@Bean
@ConditionalOnBean(name = "address")
public User user(Address address) {
// address must be present, otherwise a NullPointerException would occur
address.setCity("hangzhou");
address.setId(1L);
return new User("魅影", address.getCity());
}2.2 @ConditionalOnMissingBean
Creates a bean only when the specified type is absent.
@Configuration
public class BeanConfig {
@Bean(name = "notebookPC")
public Computer computer1() {
return new Computer("笔记本电脑");
}
@ConditionalOnMissingBean(Computer.class)
@Bean("reservePC")
public Computer computer2() {
return new Computer("备用电脑");
}
}When no parameters are supplied, the annotation applies only to a @Bean method; the bean type name becomes the default value for the type attribute.
2.3 @ConditionalOnClass
Instantiates the bean if the given class name is present on the classpath.
2.4 @ConditionalOnMissingClass
Instantiates the bean if the given class name is **not** present on the classpath.
2.5 @ConditionalOnProperty
Activates a configuration based on the presence and value of a property.
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnPropertyCondition.class)
public @interface ConditionalOnProperty {
// array of property names; cannot be used together with 'value'
String[] value() default {};
// prefix for property names, e.g., "spring.http.encoding"
String prefix() default "";
// full or partial property names; can be combined with prefix
String[] name() default {};
// value to compare against; if equal, the condition matches
String havingValue() default "";
// if true, condition matches when property is missing
boolean matchIfMissing() default false;
// whether to allow relaxed binding (unknown usage)
boolean relaxedNames() default true;
}The condition reads the property from application.properties. If the property is absent, matchIfMissing determines the outcome; otherwise the value is compared with havingValue. If havingValue is not set, any non‑false value enables the bean.
feign:
hystrix:
enabled: trueUsing the property above, a Feign builder is conditionally created:
@Bean
@Scope("prototype")
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "feign.hystrix.enabled")
public Feign.Builder feignHystrixBuilder() {
return HystrixFeign.builder();
}Conclusion : @Conditional and its derived annotations enable Spring Boot to register beans dynamically based on environment properties, classpath contents, or the presence of other beans, forming the core mechanism for conditional configuration.
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.
Shepherd Advanced Notes
Dedicated to sharing advanced Java technical insights, daily work snippets, and the power of persistent effort.
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.
