Understanding Spring Boot @Conditional Annotation and Custom Conditions
This article explains the evolution, usage, and customization of Spring Boot's @Conditional annotation, detailing its underlying Condition interface, execution phases, ordering mechanisms, and providing practical code examples for environment-specific bean registration in Spring applications.
Spring Boot provides a family of @Conditionalxxx annotations such as @ConditionalOnMissingBean that activate beans only when certain conditions are met. These annotations are essential for flexible framework configuration and conditional bean registration.
The examples in this article are based on Spring Boot version 2.3.4.RELEASE .
The core @Conditional annotation, introduced in Spring 4.0, can be placed on any class or method. It accepts one or more Condition classes via its value attribute. When all specified conditions return true , the annotated component is processed by the Spring container.
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
/**
* All {@link Condition} classes that must {@linkplain Condition#matches match}
* in order for the component to be registered.
*/
Class
[] value();
}The Condition interface defines a single method matches(ConditionContext context, AnnotatedTypeMetadata metadata) . Returning true indicates the condition is satisfied.
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}The ConditionContext provides access to the Spring environment, bean factory, resource loader, and class loader, enabling condition implementations to query the container state.
public interface ConditionContext {
BeanDefinitionRegistry getRegistry();
@Nullable ConfigurableListableBeanFactory getBeanFactory();
Environment getEnvironment();
ResourceLoader getResourceLoader();
@Nullable ClassLoader getClassLoader();
}To create a custom condition, implement Condition . The article shows two examples that inject different beans based on the operating system:
public class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) {
Environment environment = conditionContext.getEnvironment();
String os = environment.getProperty("os.name");
return os != null && os.contains("Windows");
}
}
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) {
Environment environment = conditionContext.getEnvironment();
String os = environment.getProperty("os.name");
return os != null && os.contains("Linux");
}
}These conditions are then used in a configuration class to register beans conditionally:
@Configuration
public class CustomConfig {
@Bean("winP")
@Conditional(WindowsCondition.class)
public Person personWin() { return new Person(); }
@Bean("LinuxP")
@Conditional(LinuxCondition.class)
public Person personLinux() { return new Person(); }
}A simple test demonstrates that only the bean matching the current OS is injected:
@SpringBootTest
class SpringbootInterceptApplicationTests {
@Autowired(required = false)
@Qualifier("winP")
private Person winP;
@Autowired(required = false)
@Qualifier("LinuxP")
private Person linP;
@Test
void contextLoads() {
System.out.println(winP);
System.out.println(linP);
}
}Condition evaluation occurs in two phases: ConfigurationPhase.PARSE_CONFIGURATION (during configuration class parsing) and ConfigurationPhase.REGISTER_BEAN (during bean registration). Some built‑in conditions, such as @ConditionalOnMissingBean , run in the registration phase because they need to inspect already‑registered beans.
The ConfigurationCondition interface extends Condition with a getConfigurationPhase() method, allowing developers to specify the desired phase.
public interface ConfigurationCondition extends Condition {
ConfigurationPhase getConfigurationPhase();
enum ConfigurationPhase { PARSE_CONFIGURATION, REGISTER_BEAN }
}When multiple conditions are declared, they are evaluated in declaration order by default. Ordering can be customized by implementing PriorityOrdered , Ordered , or using the @Order annotation.
@Order(1)
class Condition1 implements Condition { ... }
class Condition2 implements Condition, Ordered { ... }
class Condition3 implements Condition, PriorityOrdered { ... }Spring Boot also provides many ready‑made conditional annotations, such as @ConditionalOnBean , @ConditionalOnMissingBean , @ConditionalOnClass , @ConditionalOnWebApplication , and others, which simplify common conditional configuration scenarios.
In summary, the @Conditional family has evolved into a powerful mechanism for conditional bean registration in Spring Boot, and understanding its underlying interfaces, execution phases, and ordering rules is crucial for advanced framework integration.
Code Ape Tech Column
Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn
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.