文章目錄
- 前言
- 什么是條件注解
- 核心原理
- 常用條件注解詳解
- 1. @ConditionalOnClass和@ConditionalOnMissingClass
- 2. @ConditionalOnBean和@ConditionalOnMissingBean
- 3. @ConditionalOnProperty
- 應用場景:多數據源配置
- 在SpringBoot自動配置中的核心作用
- 自動配置的工作原理
- 經典自動配置示例分析
- 總結
前言
在SpringBoot開發中,我們經常會遇到這樣的場景:某些Bean只有在特定條件下才需要被創建,或者根據不同的環境加載不同的配置。SpringBoot的條件注解就是為了解決這類問題而設計的。
什么是條件注解
條件注解是SpringBoot提供的一套機制,允許我們根據特定條件來決定是否創建Bean、加載配置或執行某些邏輯。這些注解基于Spring 4.0引入的@Conditional注解,SpringBoot在此基礎上擴展了更多實用的條件注解。
核心原理
所有的條件注解都基于@Conditional注解,該注解需要指定一個Condition接口的實現類。Condition接口只有一個方法:
public interface Condition {boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
當Spring容器加載Bean時,會調用這個方法來判斷條件是否滿足。如果返回true,則創建Bean;如果返回false,則跳過。
常用條件注解詳解
1. @ConditionalOnClass和@ConditionalOnMissingClass
這兩個注解用于檢查類路徑中是否存在指定的類。
@Configuration
public class DatabaseConfig {// 當類路徑中存在DataSource類時才創建這個Bean@Bean@ConditionalOnClass(DataSource.class)public DataSource dataSource() {return new HikariDataSource();}// 當類路徑中不存在Redis相關類時才創建內存緩存@Bean@ConditionalOnMissingClass("org.springframework.data.redis.core.RedisTemplate")public CacheManager memoryCache() {return new ConcurrentMapCacheManager();}
}
2. @ConditionalOnBean和@ConditionalOnMissingBean
這兩個注解根據Spring容器中是否存在指定的Bean來決定是否創建。
@Configuration
public class ServiceConfig {// 當容器中存在UserRepository Bean時才創建UserService@Bean@ConditionalOnBean(UserRepository.class)public UserService userService(UserRepository userRepository) {return new UserServiceImpl(userRepository);}// 當容器中不存在CacheManager時才創建默認的@Bean@ConditionalOnMissingBean(CacheManager.class)public CacheManager defaultCacheManager() {return new NoOpCacheManager();}
}
3. @ConditionalOnProperty
根據配置屬性來決定是否創建Bean,這是最常用的條件注解之一。
@Configuration
public class FeatureConfig {// 當spring.feature.enabled=true時才啟用這個功能@Bean@ConditionalOnProperty(prefix = "spring.feature",name = "enabled",havingValue = "true",matchIfMissing = false // 默認false,即屬性不存在時不匹配)public FeatureService featureService() {return new FeatureServiceImpl();}
}
應用場景:多數據源配置
@Configuration
public class DataSourceConfig {@Primary@Bean@ConditionalOnProperty(name = "app.datasource.primary.enabled", havingValue = "true")public DataSource primaryDataSource() {return DataSourceBuilder.create().url("jdbc:mysql://localhost:3306/primary").build();}@Bean@ConditionalOnProperty(name = "app.datasource.secondary.enabled", havingValue = "true")public DataSource secondaryDataSource() {return DataSourceBuilder.create().url("jdbc:mysql://localhost:3306/secondary").build();}
}
在SpringBoot自動配置中的核心作用
SpringBoot的自動配置是其最核心的特性之一,而條件注解正是自動配置得以智能化的關鍵所在。讓我們深入了解條件注解在自動配置中是如何發揮作用的。
自動配置的工作原理
SpringBoot通過spring.factories文件定義了所有的自動配置類,啟動時會加載這些配置。但是,并不是所有的配置都應該被激活,這就需要條件注解來控制。
# META-INF/spring.factories示例
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
經典自動配置示例分析
讓我們看看SpringBoot內置的一些自動配置是如何使用條件注解的:
- RedisAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class) // Redis類存在時才配置
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {@Bean@ConditionalOnMissingBean(name = "redisTemplate") // 用戶沒有自定義時才創建@ConditionalOnSingleCandidate(RedisConnectionFactory.class)public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplate<Object, Object> template = new RedisTemplate<>();template.setConnectionFactory(redisConnectionFactory);return template;}@Bean@ConditionalOnMissingBean@ConditionalOnSingleCandidate(RedisConnectionFactory.class)public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {return new StringRedisTemplate(redisConnectionFactory);}
}
- DataSourceAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "javax.sql.DataSource") // 沒有數據源時才自動配置
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class,DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {@Configuration(proxyBeanMethods = false)@Conditional(EmbeddedDatabaseCondition.class) // 自定義條件判斷@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })@Import(EmbeddedDataSourceConfiguration.class)protected static class EmbeddedDatabaseConfiguration {}@Configuration(proxyBeanMethods = false)@Conditional(PooledDataSourceCondition.class)@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })@Import({ HikariConfiguration.class, TomcatConfiguration.class,DbcpConfiguration.class, OracleUcpConfiguration.class,GenericConfiguration.class, DataSourceJmxConfiguration.class })protected static class PooledDataSourceConfiguration {}
}
總結
SpringBoot的條件注解是一個非常強大的特性,它讓我們能夠構建更加靈活和智能的應用程序。通過合理使用這些注解,不僅能讓我們寫出更優雅的代碼,也能讓我們的應用在面對不同場景時表現得更加智能。希望這篇文章能幫助大家更好地理解和使用SpringBoot的條件注解。