Spring Boot AutoConfigure provides auto-configuration capabilities that automatically configure Spring applications based on jar dependencies present on the classpath
—
Spring Boot AutoConfigure provides a comprehensive set of conditional annotations that control when beans and configurations are registered. These annotations enable intelligent auto-configuration that adapts based on classpath contents, bean presence, configuration properties, and runtime environment.
// Core conditional annotations
import org.springframework.boot.autoconfigure.condition.*;
// Web-specific conditions
import org.springframework.boot.autoconfigure.web.ConditionalOnEnabledResourceChain;
// Spring Framework conditionals
import org.springframework.context.annotation.Conditional;
// For filter conditions
import jakarta.servlet.Filter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;Matches when beans meeting all specified requirements are already contained in the BeanFactory. All requirements must be met for the condition to match. This is useful for creating beans that depend on other beans being present.
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnBean {
/**
* The class types of beans that should be checked.
* All specified beans must be present for the condition to match.
* Beans that are not autowire candidates or not default candidates are ignored.
*
* @return array of bean classes to check (never null, defaults to empty)
*/
Class<?>[] value() default {};
/**
* The class type names of beans that should be checked.
* Use when the class might not be on the classpath at compile time.
*
* @return array of fully-qualified bean class names (never null, defaults to empty)
*/
String[] type() default {};
/**
* The annotation type decorating a bean that should be checked.
* Checks if any bean is annotated with the specified annotation.
* Useful for checking beans marked with custom annotations.
*
* @return array of annotation types (never null, defaults to empty)
*/
Class<? extends Annotation>[] annotation() default {};
/**
* The names of beans to check.
* Checks by bean name rather than type.
*
* @return array of bean names (never null, defaults to empty)
*/
String[] name() default {};
/**
* Strategy to decide if the application context hierarchy should be considered.
* - ALL: Search current and all ancestor contexts (default)
* - CURRENT: Search only the current context
* - ANCESTORS: Search only ancestor contexts
*
* @return search strategy (never null, default: SearchStrategy.ALL)
*/
SearchStrategy search() default SearchStrategy.ALL;
/**
* Additional classes that may contain the specified bean types within their generic parameters.
* For example, value=Name.class and parameterizedContainer=NameRegistration.class
* would detect both Name and NameRegistration<Name>.
*
* @return array of parameterized container classes (never null, defaults to empty)
*/
Class<?>[] parameterizedContainer() default {};
}Usage examples:
@Configuration
public class MyAutoConfiguration {
// Match when DataSource bean exists
@Bean
@ConditionalOnBean(DataSource.class)
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
// Match when bean with specific name exists
@Bean
@ConditionalOnBean(name = "myCustomBean")
public MyService myService() {
return new MyService();
}
// Match when any bean with annotation exists
@Bean
@ConditionalOnBean(annotation = MyMarkerAnnotation.class)
public FeatureService featureService() {
return new FeatureService();
}
// Match parameterized container
@Bean
@ConditionalOnBean(
value = MyEntity.class,
parameterizedContainer = EntityManager.class
)
public EntityProcessor entityProcessor() {
return new EntityProcessor();
}
}Matches when no beans meeting the specified requirements are already contained in the BeanFactory. None of the requirements must be met for the condition to match. This is the most commonly used condition for allowing user overrides.
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnMissingBean {
/**
* The class types of beans that should be checked.
* The condition matches when no beans of the specified types exist.
*
* @return array of bean classes to check (never null, defaults to empty)
*/
Class<?>[] value() default {};
/**
* The class type names of beans that should be checked.
* Use when the class might not be on the classpath at compile time.
*
* @return array of fully-qualified bean class names (never null, defaults to empty)
*/
String[] type() default {};
/**
* The class types of beans that should be ignored when identifying matching beans.
* Useful for excluding specific implementations from the check.
*
* @return array of classes to ignore (never null, defaults to empty)
*/
Class<?>[] ignored() default {};
/**
* The class type names of beans that should be ignored when identifying matching beans.
*
* @return array of fully-qualified class names to ignore (never null, defaults to empty)
*/
String[] ignoredType() default {};
/**
* The annotation type decorating a bean that should be checked.
* The condition matches when no beans with this annotation exist.
*
* @return array of annotation types (never null, defaults to empty)
*/
Class<? extends Annotation>[] annotation() default {};
/**
* The names of beans to check.
* The condition matches when beans with these names don't exist.
*
* @return array of bean names (never null, defaults to empty)
*/
String[] name() default {};
/**
* Strategy to decide if the application context hierarchy should be considered.
*
* @return search strategy (never null, default: SearchStrategy.ALL)
*/
SearchStrategy search() default SearchStrategy.ALL;
/**
* Additional classes that may contain the specified bean types within their generic parameters.
*
* @return array of parameterized container classes (never null, defaults to empty)
*/
Class<?>[] parameterizedContainer() default {};
}Usage examples:
@Configuration
public class MyAutoConfiguration {
// Provide default bean if user hasn't defined one
@Bean
@ConditionalOnMissingBean
public MyService myService() {
return new MyService();
}
// Provide default DataSource if none exists
@Bean
@ConditionalOnMissingBean(DataSource.class)
public DataSource dataSource() {
return new EmbeddedDatabase();
}
// Ignore specific implementations
@Bean
@ConditionalOnMissingBean(
value = CacheManager.class,
ignored = NoOpCacheManager.class
)
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager();
}
}Matches when a bean of the specified class is already contained in the BeanFactory and a single candidate can be determined. The condition also matches if multiple instances are found but one is marked as @Primary.
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnSingleCandidate {
/**
* The bean type that should be checked.
* The condition matches when exactly one bean of this type exists,
* or when multiple beans exist but one is marked as @Primary.
*
* @return the bean class (must not be Object.class)
*/
Class<?> value();
/**
* The bean type name that should be checked.
* Use when the class might not be on the classpath at compile time.
*
* @return fully-qualified class name (never null, defaults to empty)
*/
String type() default "";
/**
* Strategy to decide if the application context hierarchy should be considered.
*
* @return search strategy (never null, default: SearchStrategy.ALL)
*/
SearchStrategy search() default SearchStrategy.ALL;
}Usage example:
@Configuration
public class DataSourceDependentConfiguration {
// Requires exactly one DataSource bean (or one primary)
@Bean
@ConditionalOnSingleCandidate(DataSource.class)
public DataSourceHealthIndicator dataSourceHealthIndicator(
DataSource dataSource) {
return new DataSourceHealthIndicator(dataSource);
}
}Matches when no Filter beans of the specified type are contained in the BeanFactory. This condition detects both directly registered Filter beans and those registered through FilterRegistrationBean.
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ConditionalOnMissingBean(parameterizedContainer = FilterRegistrationBean.class)
public @interface ConditionalOnMissingFilterBean {
/**
* The filter bean type that must not be present.
* Checks for both direct Filter beans and FilterRegistrationBean<Filter>.
*
* @return array of filter classes (never null, defaults to empty)
*/
Class<? extends Filter>[] value() default {};
}Usage example:
@Configuration
public class MyFilterConfiguration {
@Bean
@ConditionalOnMissingFilterBean(CorsFilter.class)
public FilterRegistrationBean<CorsFilter> corsFilter() {
FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(new CorsFilter());
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
}Matches when the specified classes are on the classpath. Safe to use on @Configuration classes as annotation metadata is parsed using ASM before class loading. This is one of the most commonly used conditions.
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {
/**
* The classes that must be present on the classpath.
* Since this annotation is parsed by loading class bytecode, it is safe to specify
* classes that may ultimately not be on the classpath, but only if this annotation
* is directly on the affected component and not used as a meta-annotation.
* When used as a meta-annotation, use name() instead.
*
* @return array of classes that must be present (never null, defaults to empty)
*/
Class<?>[] value() default {};
/**
* The class names that must be present on the classpath.
* Use when you cannot reference the class directly (e.g., in meta-annotations
* or when the class might not be available at compile time).
* Must be fully-qualified class names.
*
* @return array of fully-qualified class names (never null, defaults to empty)
*/
String[] name() default {};
}Usage examples:
// On configuration class
@Configuration
@ConditionalOnClass(name = "javax.sql.DataSource")
public class DataSourceAutoConfiguration {
@Configuration
@ConditionalOnClass(HikariDataSource.class)
static class HikariConfiguration {
@Bean
public DataSource dataSource() {
return new HikariDataSource();
}
}
}
// Multiple class check
@Configuration
@ConditionalOnClass({
DataSource.class,
JdbcTemplate.class,
PlatformTransactionManager.class
})
public class JdbcConfiguration {
// Configuration
}
// Using class names for optional dependencies
@Configuration
@ConditionalOnClass(name = {
"org.hibernate.SessionFactory",
"jakarta.persistence.EntityManagerFactory"
})
public class JpaConfiguration {
// Configuration
}Matches when the specified classes are not on the classpath. Useful for providing fallback configurations or excluding features when optional dependencies are absent.
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnMissingClass {
/**
* The class names that must not be present on the classpath.
* Must be fully-qualified class names.
*
* @return array of fully-qualified class names (never null, must not be empty)
*/
String[] value();
}Usage examples:
@Configuration
public class FallbackConfiguration {
// Use simple implementation when Caffeine is not available
@Bean
@ConditionalOnMissingClass("com.github.benmanes.caffeine.cache.Caffeine")
public CacheManager simpleCacheManager() {
return new ConcurrentMapCacheManager();
}
// Provide mock when test library is not available
@Bean
@ConditionalOnMissingClass({
"org.mockito.Mockito",
"org.easymock.EasyMock"
})
public TestSupport basicTestSupport() {
return new BasicTestSupport();
}
}Checks if the specified properties have a specific value. By default, properties must be present in the Environment and not equal to "false". This is very useful for feature flags and environment-specific configuration.
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnPropertyCondition.class)
@Repeatable(ConditionalOnProperties.class)
public @interface ConditionalOnProperty {
/**
* Alias for name(). Use either value() or name(), not both.
*
* @return array of property names (never null, defaults to empty)
*/
String[] value() default {};
/**
* A prefix that should be applied to each property.
* The prefix automatically ends with a dot if not specified.
* For example, prefix="app.feature" with name="enabled" checks "app.feature.enabled".
*
* @return property prefix (never null, defaults to empty string)
*/
String prefix() default "";
/**
* The name of the properties to test.
* If a prefix has been defined, it is applied to compute the full key.
* Use the dashed notation (e.g. "my-long-property") for consistency with Spring Boot.
*
* @return array of property names (never null, defaults to empty)
*/
String[] name() default {};
/**
* The string representation of the expected value for the properties.
* If not specified, the property must not be equal to "false".
* Empty string is a valid value.
*
* @return expected property value (never null, defaults to empty string)
*/
String havingValue() default "";
/**
* Specify if the condition should match if the property is not set.
* Defaults to false (property must be set for condition to match).
* Set to true for "opt-out" behavior.
*
* @return true to match when property is missing, false otherwise (default: false)
*/
boolean matchIfMissing() default false;
}Usage examples:
@Configuration
public class FeatureConfiguration {
// Simple property check (enabled by default)
@Configuration
@ConditionalOnProperty(
prefix = "spring.datasource",
name = "enabled",
havingValue = "true",
matchIfMissing = true
)
public static class DataSourceConfiguration {
// Configuration beans
}
// Multiple properties (all must match)
@Configuration
@ConditionalOnProperty(
prefix = "app.features",
name = {"caching", "monitoring"},
havingValue = "enabled"
)
public static class CachingAndMonitoringConfiguration {
// Configuration
}
// Opt-out feature (enabled unless explicitly disabled)
@Bean
@ConditionalOnProperty(
name = "app.security.csrf.enabled",
matchIfMissing = true
)
public CsrfTokenRepository csrfTokenRepository() {
return new HttpSessionCsrfTokenRepository();
}
}Container annotation for multiple @ConditionalOnProperty annotations. Allows combining multiple property conditions with AND logic.
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnPropertyCondition.class)
public @interface ConditionalOnProperties {
/**
* The array of @ConditionalOnProperty annotations.
* All property conditions must be satisfied for the overall condition to match.
*
* @return array of property conditions (never null, must not be empty)
*/
ConditionalOnProperty[] value();
}Checks if the specified properties have a specific boolean value. Similar to @ConditionalOnProperty but specifically for boolean values with proper type conversion.
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnBooleanPropertyCondition.class)
@Repeatable(ConditionalOnBooleanProperties.class)
public @interface ConditionalOnBooleanProperty {
/**
* Alias for name(). Use either value() or name(), not both.
*
* @return array of property names (never null, defaults to empty)
*/
String[] value() default {};
/**
* A prefix that should be applied to each property.
*
* @return property prefix (never null, defaults to empty string)
*/
String prefix() default "";
/**
* The name of the properties to test.
*
* @return array of property names (never null, defaults to empty)
*/
String[] name() default {};
/**
* The boolean value that the properties should have.
*
* @return expected boolean value (default: true)
*/
boolean havingValue() default true;
/**
* Specify if the condition should match if the property is not set.
* Defaults to false (property must be set).
*
* @return true to match when property is missing, false otherwise (default: false)
*/
boolean matchIfMissing() default false;
}Container annotation for multiple @ConditionalOnBooleanProperty annotations.
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnBooleanPropertyCondition.class)
public @interface ConditionalOnBooleanProperties {
/**
* The array of @ConditionalOnBooleanProperty annotations.
*
* @return array of boolean property conditions (never null, must not be empty)
*/
ConditionalOnBooleanProperty[] value();
}Matches when the specified resources are on the classpath. Useful for checking if configuration files or other resources exist.
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnResourceCondition.class)
public @interface ConditionalOnResource {
/**
* The resources that must be present.
* Each resource location should be specified using Spring's resource location format.
* Supports classpath:, file:, http:, and other prefixes.
*
* Examples:
* - "classpath:myfile.properties"
* - "file:/path/to/file"
* - "classpath*:config/*.xml" (with wildcards)
*
* @return array of resource locations (never null, must not be empty)
*/
String[] resources();
}Usage examples:
@Configuration
@ConditionalOnResource(resources = "classpath:custom-config.properties")
public class CustomConfiguration {
// Configuration beans
}
// Multiple resources (all must exist)
@Configuration
@ConditionalOnResource(resources = {
"classpath:app.properties",
"classpath:db-config.xml"
})
public class FileBasedConfiguration {
// Configuration
}
// With wildcards
@Configuration
@ConditionalOnResource(resources = "classpath*:META-INF/plugins/*.xml")
public class PluginConfiguration {
// Load plugins from resources
}Configuration annotation for a conditional element that depends on the value of a SpEL expression. Provides maximum flexibility but should be used sparingly as it's slower than type-safe conditions and harder to test.
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnExpressionCondition.class)
public @interface ConditionalOnExpression {
/**
* The SpEL expression to evaluate.
* Expression should return a boolean value.
* Referencing a bean in the expression will cause that bean to be initialized very early,
* so use with caution.
*
* Available context:
* - Environment properties: ${property.name}
* - System properties: systemProperties['os.name']
* - Bean references: @beanName (causes early initialization)
*
* @return SpEL expression string (never null, must not be empty)
*/
String value();
}Usage examples:
@Configuration
@ConditionalOnExpression("${feature.enabled:false} and '${feature.name}' == 'myfeature'")
public class FeatureConfiguration {
// Configuration beans
}
// Complex expression
@Bean
@ConditionalOnExpression(
"#{systemProperties['os.name'].toLowerCase().contains('windows')} " +
"and ${app.windows.features.enabled:false}"
)
public WindowsSpecificService windowsService() {
return new WindowsSpecificService();
}
// Avoid: Bean reference causes early initialization
@Bean
@ConditionalOnExpression("@myService.isReady()") // DON'T DO THIS
public DependentService dependentService() {
return new DependentService();
}Matches when the application is a web application. Can be narrowed using the type attribute to check for specific web application types (servlet, reactive).
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnWebApplicationCondition.class)
public @interface ConditionalOnWebApplication {
/**
* The type of web application to check.
*
* @return web application type (never null, default: Type.ANY)
*/
Type type() default Type.ANY;
/**
* Available web application types.
*/
enum Type {
/**
* Any web application (servlet or reactive).
*/
ANY,
/**
* Servlet-based web application.
* Requires ServletWebServerFactory on classpath.
*/
SERVLET,
/**
* Reactive web application.
* Requires ReactiveWebApplicationContext.
*/
REACTIVE
}
}Usage examples:
@Configuration
@ConditionalOnWebApplication
public class WebConfiguration {
// Applies to any web application
}
@Configuration
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
public class ServletWebConfiguration {
// Servlet-specific configuration
@Bean
public FilterRegistrationBean<MyFilter> myFilter() {
return new FilterRegistrationBean<>(new MyFilter());
}
}
@Configuration
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
public class ReactiveWebConfiguration {
// Reactive-specific configuration
@Bean
public WebFilter myWebFilter() {
return (exchange, chain) -> {
// Reactive filter logic
return chain.filter(exchange);
};
}
}Matches when the application context is not a web application context. Useful for configuring non-web features or command-line applications.
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnWebApplicationCondition.class)
public @interface ConditionalOnNotWebApplication {
}Usage example:
@Configuration
@ConditionalOnNotWebApplication
public class CommandLineConfiguration {
@Bean
public CommandLineRunner myCommandLineRunner() {
return args -> {
System.out.println("Running as command-line application");
};
}
}Matches when the application is a traditional WAR deployment. For applications with embedded servers, this condition returns false.
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnWarDeploymentCondition.class)
public @interface ConditionalOnWarDeployment {
}Matches when the application is not a traditional WAR deployment. For applications with embedded servers, this condition returns true.
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnWarDeploymentCondition.class)
public @interface ConditionalOnNotWarDeployment {
}Usage example:
@Configuration
public class DeploymentConfiguration {
@Configuration
@ConditionalOnWarDeployment
static class WarDeploymentConfiguration {
@Bean
public ServletContextInitializer warInitializer() {
return servletContext -> {
// WAR-specific initialization
};
}
}
@Configuration
@ConditionalOnNotWarDeployment
static class EmbeddedServerConfiguration {
@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory>
embeddedCustomizer() {
return factory -> {
// Embedded server customization
};
}
}
}Matches based on the JVM version the application is running on. Useful for enabling features that require specific Java versions.
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnJavaCondition.class)
public @interface ConditionalOnJava {
/**
* The range of Java versions to match.
*
* @return range type (never null, default: Range.EQUAL_OR_NEWER)
*/
Range range() default Range.EQUAL_OR_NEWER;
/**
* The Java version to check.
*
* @return Java version (never null)
*/
JavaVersion value();
/**
* Range options for Java version checks.
*/
enum Range {
/**
* Match if running Java version is equal to or newer than specified.
*/
EQUAL_OR_NEWER,
/**
* Match if running Java version is older than specified.
*/
OLDER_THAN
}
}Usage examples:
@Configuration
@ConditionalOnJava(JavaVersion.SEVENTEEN)
public class Java17Configuration {
// Java 17+ specific configuration
@Bean
public RecordBasedService recordService() {
// Use Java records
return new RecordBasedService();
}
}
@Configuration
@ConditionalOnJava(
value = JavaVersion.TWENTY_ONE,
range = ConditionalOnJava.Range.EQUAL_OR_NEWER
)
public class VirtualThreadConfiguration {
@Bean
public Executor virtualThreadExecutor() {
return Executors.newVirtualThreadPerTaskExecutor();
}
}
@Configuration
@ConditionalOnJava(
value = JavaVersion.TWENTY_ONE,
range = ConditionalOnJava.Range.OLDER_THAN
)
public class LegacyThreadConfiguration {
@Bean
public Executor platformThreadExecutor() {
return Executors.newFixedThreadPool(10);
}
}Matches when the specified cloud platform is active. Spring Boot automatically detects common cloud platforms based on environment variables and system properties.
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnCloudPlatformCondition.class)
public @interface ConditionalOnCloudPlatform {
/**
* The cloud platform that must be active.
*
* @return cloud platform type (never null)
*/
CloudPlatform value();
}Usage examples:
@Configuration
@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)
public class KubernetesConfiguration {
@Bean
public ServiceDiscovery kubernetesServiceDiscovery() {
return new KubernetesServiceDiscovery();
}
}
@Configuration
@ConditionalOnCloudPlatform(CloudPlatform.CLOUD_FOUNDRY)
public class CloudFoundryConfiguration {
@Bean
public CloudFoundryVcapProcessor vcapProcessor() {
return new CloudFoundryVcapProcessor();
}
}Matches based on the availability of a JNDI InitialContext and the ability to lookup specific locations. Useful for enterprise application servers.
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnJndiCondition.class)
public @interface ConditionalOnJndi {
/**
* JNDI locations to check.
* If no locations are specified, the condition matches if JNDI is available.
* Locations should be JNDI names like "java:comp/env/jdbc/myDataSource".
*
* @return array of JNDI locations (never null, defaults to empty)
*/
String[] value() default {};
}Usage examples:
// Check if JNDI is available
@Configuration
@ConditionalOnJndi
public class JndiConfiguration {
// JNDI-based configuration
}
// Check for specific JNDI resources
@Bean
@ConditionalOnJndi("java:comp/env/jdbc/myDataSource")
public DataSource jndiDataSource() throws NamingException {
Context ctx = new InitialContext();
return (DataSource) ctx.lookup("java:comp/env/jdbc/myDataSource");
}Matches when the specified threading model is active. Useful for configuring virtual threads vs platform threads (Java 21+).
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnThreadingCondition.class)
public @interface ConditionalOnThreading {
/**
* The threading model that should be active.
*
* @return threading type (never null)
*/
Threading value();
}Usage example:
@Configuration
public class ThreadingConfiguration {
@Bean
@ConditionalOnThreading(Threading.VIRTUAL)
public Executor virtualThreadExecutor() {
return Executors.newVirtualThreadPerTaskExecutor();
}
@Bean
@ConditionalOnThreading(Threading.PLATFORM)
public Executor platformThreadExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(50);
executor.initialize();
return executor;
}
}Matches when coordinated restore at checkpoint is to be used (Project CRaC support). Useful for configuring fast startup with CRaC.
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnCheckpointRestoreCondition.class)
public @interface ConditionalOnCheckpointRestore {
}Matches when the Spring resource handling chain is enabled. Matches if spring.web.resources.chain.enabled is true or if webjars-locator libraries are on the classpath.
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnEnabledResourceChainCondition.class)
public @interface ConditionalOnEnabledResourceChain {
}Usage example:
@Configuration
@ConditionalOnEnabledResourceChain
public class ResourceChainConfiguration {
@Bean
public ResourceUrlEncodingFilter resourceUrlEncodingFilter() {
return new ResourceUrlEncodingFilter();
}
}Base class for Spring Boot conditions. Subclass and implement getMatchOutcome() to create custom conditions.
public abstract class SpringBootCondition implements Condition {
/**
* Determine if the condition matches.
* Final method that delegates to getMatchOutcome().
*
* @param context the condition context (never null)
* @param metadata the metadata of the class or method (never null)
* @return true if the condition matches, false otherwise
*/
public final boolean matches(
ConditionContext context,
AnnotatedTypeMetadata metadata
);
/**
* Determine the outcome of the match.
* Subclasses must implement this method to define matching logic.
*
* @param context the condition context (never null)
* @param metadata the metadata of the class or method (never null)
* @return the condition outcome (never null)
*/
public abstract ConditionOutcome getMatchOutcome(
ConditionContext context,
AnnotatedTypeMetadata metadata
);
}Outcome for a condition match, including a log message for the condition evaluation report.
public class ConditionOutcome {
/**
* Create a new ConditionOutcome indicating a match.
*
* @return new matching outcome (never null)
*/
public static ConditionOutcome match();
/**
* Create a new ConditionOutcome indicating a match with a message.
*
* @param message the outcome message (never null)
* @return new matching outcome (never null)
*/
public static ConditionOutcome match(String message);
/**
* Create a new ConditionOutcome indicating a match with a ConditionMessage.
*
* @param message the condition message (never null)
* @return new matching outcome (never null)
*/
public static ConditionOutcome match(ConditionMessage message);
/**
* Create a new ConditionOutcome indicating no match with a message.
*
* @param message the outcome message (never null)
* @return new non-matching outcome (never null)
*/
public static ConditionOutcome noMatch(String message);
/**
* Create a new ConditionOutcome indicating no match with a ConditionMessage.
*
* @param message the condition message (never null)
* @return new non-matching outcome (never null)
*/
public static ConditionOutcome noMatch(ConditionMessage message);
/**
* Return true if the outcome was a match.
*
* @return true if matched, false otherwise
*/
public boolean isMatch();
/**
* Return the condition message.
*
* @return the message (never null)
*/
public ConditionMessage getConditionMessage();
}Builder for creating condition messages with consistent formatting.
public final class ConditionMessage {
/**
* Check if the message is empty.
*
* @return true if empty, false otherwise
*/
public boolean isEmpty();
/**
* Append a message to this message.
*
* @param message the message to append (never null)
* @return new condition message with appended content (never null)
*/
public ConditionMessage append(String message);
/**
* Create an empty condition message.
*
* @return empty message (never null)
*/
public static ConditionMessage empty();
/**
* Create a condition message from a formatted string.
*
* @param message the message format (never null)
* @param args the format arguments
* @return new condition message (never null)
*/
public static ConditionMessage of(String message, Object... args);
/**
* Create a condition message from a collection of messages.
*
* @param messages the messages to combine (never null)
* @return new condition message (never null)
*/
public static ConditionMessage of(Collection<? extends ConditionMessage> messages);
/**
* Create a builder for a condition with the given annotation.
*
* @param condition the condition annotation class (never null)
* @param details additional details about the condition
* @return new builder (never null)
*/
public static Builder forCondition(Class<? extends Annotation> condition, Object... details);
/**
* Create a builder for a condition with the given name.
*
* @param condition the condition name (never null)
* @param details additional details about the condition
* @return new builder (never null)
*/
public static Builder forCondition(String condition, Object... details);
/**
* Builder for constructing condition messages.
*/
public static class Builder {
/**
* Indicates that an exact match was found.
*
* @param result the matched result (never null)
* @return new condition message (never null)
*/
public ConditionMessage foundExactly(Object result);
/**
* Indicates that items were found.
*
* @param article the article to use ("a", "an", "the", etc.)
* @return items builder (never null)
*/
public ItemsBuilder found(String article);
/**
* Indicates that items were found with singular and plural forms.
*
* @param singular the singular form (e.g., "bean")
* @param plural the plural form (e.g., "beans")
* @return items builder (never null)
*/
public ItemsBuilder found(String singular, String plural);
/**
* Indicates that items were not found.
*
* @param article the article to use
* @return items builder (never null)
*/
public ItemsBuilder didNotFind(String article);
/**
* Indicates that items were not found with singular and plural forms.
*
* @param singular the singular form
* @param plural the plural form
* @return items builder (never null)
*/
public ItemsBuilder didNotFind(String singular, String plural);
}
/**
* Builder for constructing condition messages with items.
*/
public static final class ItemsBuilder {
/**
* Used when no items are available.
* For example: didNotFind("any beans").atAll() results in "did not find any beans".
*
* @return new condition message (never null)
*/
public ConditionMessage atAll();
/**
* Indicate the items with normal styling.
* For example: didNotFind("bean", "beans").items("x", "y") results in "did not find beans x, y".
*
* @param items the items to include
* @return new condition message (never null)
*/
public ConditionMessage items(Object... items);
/**
* Indicate the items with a specific style.
* For example: didNotFind("bean", "beans").items(Style.QUOTE, "x") results in "did not find bean 'x'".
*
* @param style the rendering style (never null)
* @param items the items to include
* @return new condition message (never null)
*/
public ConditionMessage items(Style style, Object... items);
/**
* Indicate the items from a collection with normal styling.
*
* @param items the items collection (never null)
* @return new condition message (never null)
*/
public ConditionMessage items(Collection<?> items);
/**
* Indicate the items from a collection with a specific style.
*
* @param style the rendering style (never null)
* @param items the items collection (never null)
* @return new condition message (never null)
*/
public ConditionMessage items(Style style, Collection<?> items);
}
/**
* Rendering styles for condition message items.
*/
public enum Style {
/**
* Render with normal styling (no quotes).
* Example: bean1, bean2, bean3
*/
NORMAL,
/**
* Render with the item surrounded by single quotes.
* Example: 'bean1', 'bean2', 'bean3'
*/
QUOTE
}
}Custom condition example:
public class MyCustomCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(
ConditionContext context,
AnnotatedTypeMetadata metadata) {
String value = context.getEnvironment().getProperty("my.property");
if (value != null && value.startsWith("custom-")) {
return ConditionOutcome.match(
ConditionMessage.forCondition("MyCustomCondition")
.found("property")
.items(Style.QUOTE, value)
);
}
return ConditionOutcome.noMatch(
ConditionMessage.forCondition("MyCustomCondition")
.didNotFind("matching property")
.atAll()
);
}
}Base class for conditions that can evaluate nested conditions with different matching strategies.
public abstract class AbstractNestedCondition extends SpringBootCondition
implements ConfigurationCondition {
/**
* Create a new AbstractNestedCondition.
*
* @param configurationPhase the configuration phase (never null)
*/
public AbstractNestedCondition(ConfigurationPhase configurationPhase);
/**
* Get the configuration phase for this condition.
*
* @return the configuration phase (never null)
*/
@Override
public ConfigurationPhase getConfigurationPhase();
/**
* Evaluate nested conditions and determine the match outcome.
*
* @param context the condition context (never null)
* @param metadata the metadata of the class or method (never null)
* @return the condition outcome (never null)
*/
@Override
public ConditionOutcome getMatchOutcome(
ConditionContext context,
AnnotatedTypeMetadata metadata
);
/**
* Represents the evaluation of nested conditions.
*/
protected static class MemberMatchOutcomes {
/**
* Get all member outcomes.
*
* @return list of all outcomes (never null)
*/
public List<ConditionOutcome> getAll();
/**
* Get matching member outcomes.
*
* @return list of matching outcomes (never null)
*/
public List<ConditionOutcome> getMatches();
/**
* Get non-matching member outcomes.
*
* @return list of non-matching outcomes (never null)
*/
public List<ConditionOutcome> getNonMatches();
}
}A nested condition that matches when all nested class conditions match (logical AND).
public abstract class AllNestedConditions extends AbstractNestedCondition {
/**
* Create a new AllNestedConditions.
*
* @param configurationPhase the configuration phase (never null)
*/
public AllNestedConditions(ConfigurationPhase configurationPhase);
}Usage example:
@Configuration
@Conditional(MyAllConditions.class)
public class MyConfiguration {
static class MyAllConditions extends AllNestedConditions {
MyAllConditions() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnProperty("feature.enabled")
static class FeatureEnabled {}
@ConditionalOnClass(name = "com.example.SomeClass")
static class ClassPresent {}
@ConditionalOnBean(DataSource.class)
static class DataSourcePresent {}
}
}A nested condition that matches when any nested class condition matches (logical OR).
public abstract class AnyNestedCondition extends AbstractNestedCondition {
/**
* Create a new AnyNestedCondition.
*
* @param configurationPhase the configuration phase (never null)
*/
public AnyNestedCondition(ConfigurationPhase configurationPhase);
}Usage example:
@Configuration
@Conditional(DatabaseConditions.class)
public class DatabaseConfiguration {
static class DatabaseConditions extends AnyNestedCondition {
DatabaseConditions() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnProperty(name = "database.type", havingValue = "mysql")
static class MySQLEnabled {}
@ConditionalOnProperty(name = "database.type", havingValue = "postgresql")
static class PostgreSQLEnabled {}
@ConditionalOnProperty(name = "database.type", havingValue = "oracle")
static class OracleEnabled {}
}
}A nested condition that matches when none of the nested class conditions match (logical NOR).
public abstract class NoneNestedConditions extends AbstractNestedCondition {
/**
* Create a new NoneNestedConditions.
*
* @param configurationPhase the configuration phase (never null)
*/
public NoneNestedConditions(ConfigurationPhase configurationPhase);
}Defines strategies for searching beans in the bean factory hierarchy.
public enum SearchStrategy {
/**
* Search only the current context.
* Fastest option but might miss beans in parent contexts.
*/
CURRENT,
/**
* Search all ancestor contexts but not the current context.
* Useful when you only want beans from parent contexts.
*/
ANCESTORS,
/**
* Search the current context and all ancestors.
* Default strategy for most conditions.
* Most thorough but slightly slower.
*/
ALL
}Enum representing known Java versions. From org.springframework.boot.system package.
public enum JavaVersion {
SEVENTEEN, // Java 17 LTS
EIGHTEEN, // Java 18
NINETEEN, // Java 19
TWENTY, // Java 20
TWENTY_ONE, // Java 21 LTS - Virtual Threads, Pattern Matching
TWENTY_TWO, // Java 22
TWENTY_THREE, // Java 23
TWENTY_FOUR, // Java 24
TWENTY_FIVE // Java 25
}Enum for detecting well-known cloud platforms. From org.springframework.boot.cloud package.
public enum CloudPlatform {
/**
* No cloud platform detected.
*/
NONE,
/**
* Cloud Foundry platform.
* Detected via VCAP_APPLICATION or VCAP_SERVICES environment variables.
*/
CLOUD_FOUNDRY,
/**
* Heroku platform.
* Detected via DYNO environment variable.
*/
HEROKU,
/**
* SAP Cloud Platform (formerly SAP Cloud Platform Neo).
* Detected via HC_LANDSCAPE environment variable.
*/
SAP,
/**
* Kubernetes platform.
* Detected via KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT environment variables.
*/
KUBERNETES,
/**
* Microsoft Azure App Service.
* Detected via WEBSITE_INSTANCE_ID environment variable.
*/
AZURE_APP_SERVICE
}Enum representing threading models. From org.springframework.boot.thread package.
public enum Threading {
/**
* Platform threads (traditional threads).
* Default threading model.
*/
PLATFORM,
/**
* Virtual threads (Java 21+).
* Lightweight threads managed by the JVM.
* Much more scalable for I/O-bound workloads.
*/
VIRTUAL
}Records condition evaluation details for all auto-configurations. Provides access to condition matches, mismatches, and exclusions for diagnostics and logging.
public final class ConditionEvaluationReport {
/**
* Find the report for the given bean factory.
* Returns null if no report is found.
*
* @param beanFactory the bean factory (never null)
* @return the report or null if not found
*/
public static ConditionEvaluationReport find(BeanFactory beanFactory);
/**
* Obtain or create the report for the given bean factory.
*
* @param beanFactory the bean factory (never null)
* @return the report (never null)
*/
public static ConditionEvaluationReport get(
ConfigurableListableBeanFactory beanFactory
);
/**
* Record condition evaluation for a source configuration class.
*
* @param source the source configuration class name (never null)
* @param condition the condition that was evaluated (never null)
* @param outcome the outcome of the evaluation (never null)
*/
public void recordConditionEvaluation(
String source,
Condition condition,
ConditionOutcome outcome
);
/**
* Record classes that were excluded from auto-configuration.
*
* @param exclusions the excluded class names (never null)
*/
public void recordExclusions(Collection<String> exclusions);
/**
* Record classes that were candidates for condition evaluation.
*
* @param evaluationCandidates the candidate class names (never null)
*/
public void recordEvaluationCandidates(List<String> evaluationCandidates);
/**
* Get condition evaluation results grouped by source configuration class.
* Returns a map where keys are configuration class names and values
* are ConditionAndOutcomes containing all condition evaluations for that class.
*
* @return map of source to condition evaluations (never null)
*/
public Map<String, ConditionAndOutcomes> getConditionAndOutcomesBySource();
/**
* Get the list of classes that were excluded from auto-configuration.
*
* @return list of excluded class names (never null)
*/
public List<String> getExclusions();
/**
* Get the set of unconditional classes that were processed.
* These are classes that have no conditions.
*
* @return set of unconditional class names (never null)
*/
public Set<String> getUnconditionalClasses();
/**
* Get the parent report if this is from a child application context.
*
* @return parent report or null if no parent
*/
public ConditionEvaluationReport getParent();
/**
* Get differences between this report and a previous report.
* Useful for incremental reporting.
*
* @param previousReport the previous report (never null)
* @return delta report (never null)
*/
public ConditionEvaluationReport getDelta(ConditionEvaluationReport previousReport);
}Container for multiple condition evaluations associated with a single configuration class.
public static class ConditionAndOutcomes implements Iterable<ConditionAndOutcome> {
/**
* Add a condition and outcome to this collection.
*
* @param condition the condition (never null)
* @param outcome the outcome (never null)
*/
public void add(Condition condition, ConditionOutcome outcome);
/**
* Check if all conditions matched (full match).
*
* @return true if all conditions matched, false otherwise
*/
public boolean isFullMatch();
/**
* Get a stream of all condition and outcome pairs.
*
* @return stream of condition/outcome pairs (never null)
*/
public Stream<ConditionAndOutcome> stream();
/**
* Iterate over all condition and outcome pairs.
*
* @return iterator (never null)
*/
public Iterator<ConditionAndOutcome> iterator();
}Represents a single condition evaluation result pairing a condition with its outcome.
public static class ConditionAndOutcome {
/**
* Get the condition that was evaluated.
*
* @return the condition (never null)
*/
public Condition getCondition();
/**
* Get the outcome of the condition evaluation.
*
* @return the outcome (never null)
*/
public ConditionOutcome getOutcome();
}Usage example:
import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport;
import org.springframework.context.ConfigurableApplicationContext;
public class ConditionReportAnalyzer {
public void analyzeConditions(ConfigurableApplicationContext context) {
ConditionEvaluationReport report = ConditionEvaluationReport.get(
context.getBeanFactory()
);
// Get all condition evaluations
Map<String, ConditionAndOutcomes> results =
report.getConditionAndOutcomesBySource();
// Analyze each configuration class
results.forEach((source, outcomes) -> {
System.out.println("Configuration: " + source);
System.out.println("Full match: " + outcomes.isFullMatch());
outcomes.forEach(conditionAndOutcome -> {
ConditionOutcome outcome = conditionAndOutcome.getOutcome();
System.out.println(" - " + outcome.getConditionMessage());
});
});
// Check exclusions
Set<String> exclusions = report.getExclusions();
System.out.println("Excluded configurations: " + exclusions);
}
}Place fastest conditions first to fail fast:
// Good: Fast conditions first
@Configuration
@ConditionalOnClass(DataSource.class) // Fast: checks class presence
@ConditionalOnProperty("app.enabled") // Fast: checks property
@ConditionalOnBean(DataSource.class) // Slower: checks bean registry
public class MyConfiguration {
// Configuration
}Prefer type-safe conditions over expressions:
// Good
@ConditionalOnClass(MyService.class)
@ConditionalOnProperty(prefix = "app", name = "enabled")
// Avoid
@ConditionalOnExpression("${app.enabled:false} and T(MyService).class != null")Use multiple annotations for AND logic, nested conditions for OR logic:
// AND: All conditions must match
@ConditionalOnClass(DataSource.class)
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty("app.datasource.enabled")
// OR: Any condition must match
@Conditional(DatabaseOrCacheEnabled.class)
static class DatabaseOrCacheEnabled extends AnyNestedCondition {
DatabaseOrCacheEnabled() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnProperty("database.enabled")
static class DatabaseEnabled {}
@ConditionalOnProperty("cache.enabled")
static class CacheEnabled {}
}Always use @ConditionalOnMissingBean for beans that users might want to customize:
@Bean
@ConditionalOnMissingBean
public MyService myService() {
return new DefaultMyService();
}Add comments explaining complex conditions:
/**
* Configures HTTPS when:
* - SSL bundle is configured
* - Security is enabled
* - Not running in development mode
*/
@Configuration
@ConditionalOnProperty("spring.ssl.bundle.enabled")
@ConditionalOnProperty(name = "spring.security.enabled", matchIfMissing = true)
@ConditionalOnProperty(name = "spring.profiles.active", havingValue = "dev", matchIfMissing = true)
public class HttpsConfiguration {
// Configuration
}Test both positive and negative cases:
@Test
void testConditionMatches() {
contextRunner
.withPropertyValues("feature.enabled=true")
.run(context -> {
assertThat(context).hasBean("featureService");
});
}
@Test
void testConditionDoesNotMatch() {
contextRunner
.withPropertyValues("feature.enabled=false")
.run(context -> {
assertThat(context).doesNotHaveBean("featureService");
});
}Use matchIfMissing=true for opt-out features:
// Opt-out: Enabled by default, users must explicitly disable
@Bean
@ConditionalOnProperty(
name = "app.security.enabled",
matchIfMissing = true // Default to enabled
)
public SecurityConfig securityConfig() {
return new SecurityConfig();
}Don't reference beans in @ConditionalOnExpression:
// Bad: Causes early bean initialization
@ConditionalOnExpression("@myService.isReady()")
// Good: Use properties or other conditions
@ConditionalOnProperty("myservice.ready")@Configuration
public class FeatureFlagsConfiguration {
@Bean
@ConditionalOnProperty(
prefix = "features",
name = "new-ui",
havingValue = "enabled"
)
public NewUIController newUIController() {
return new NewUIController();
}
}@Configuration
@Profile("production")
@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)
public class ProductionKubernetesConfiguration {
// Production K8s specific configuration
}@Configuration
public class CacheConfiguration {
// Try Redis first
@Bean
@ConditionalOnClass(RedisConnectionFactory.class)
@ConditionalOnBean(RedisConnectionFactory.class)
public CacheManager redisCacheManager(RedisConnectionFactory factory) {
return RedisCacheManager.builder(factory).build();
}
// Fall back to Caffeine
@Bean
@ConditionalOnClass(Caffeine.class)
@ConditionalOnMissingBean(CacheManager.class)
public CacheManager caffeineCacheManager() {
return new CaffeineCacheManager();
}
// Final fallback
@Bean
@ConditionalOnMissingBean
public CacheManager simpleCacheManager() {
return new ConcurrentMapCacheManager();
}
}