在源碼閱讀經(jīng)常會遇到
ConditionalXX
類型的注解,它們的含義是什么倒庵?我們在開發(fā)jar時需要怎么使用褒墨?
1. @ConditionalOnMissingBean
比如我們引入了一個jar炫刷,在jar中存在一個bean,上面標(biāo)有
@ConditionalOnMissingBean
郁妈。該注解的含義便是浑玛,當(dāng)項目中存在相同類型的bean時,則不會加載jar中的bean噩咪。
案例:
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration
@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(
RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(
RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
2. @ConditionalOnProperty
當(dāng)配置文件中存在規(guī)則的配置時顾彰,才會加載bean。
注意點:
- @Bean的方法名字不能相同剧腻,否則@ConditionalOnProperty配置失效;
- 配置中使用枚舉類型時涂屁,@ConditionalOnProperty中的值是小寫书在;
- 當(dāng)配置文件中沒有對應(yīng)配置項時,可以使用@ConditionalOnProperty的
matchIfMissing = true
表示拆又,未配置時默認(rèn)初始化某bean儒旬。
場景:實現(xiàn)了Cache,可以根據(jù)配置文件的值靈活的切換緩存帖族。
@Getter
public enum CacheEnum {
MEMORY, REDIS;
}
配置文件:
@Data
@ConfigurationProperties("base")
public class BaseConfigProperties {
/**
* 緩存類型栈源,默認(rèn)內(nèi)存級別的緩存
* 默認(rèn)支持本地緩存和Redis分布式緩存
*/
private CacheEnum cache;
}
@Slf4j
@Configuration
@EnableConfigurationProperties(value = BaseConfigProperties.class)
public class OSAutoConfiguration {
@Configuration
@ConditionalOnClass(org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration.class)
@ConditionalOnProperty(name = "base.cache", havingValue = "redis")
protected static class RedisCacheManagerConfiguration {
@Bean
@ConditionalOnMissingBean
public CacheManager redisCacheManager(StringRedisTemplate stringRedisTemplate) {
log.info("啟用redis緩存...");
return new CacheManager() {
@Override
public void put(String key, String value, long time) {
stringRedisTemplate.opsForValue().set(key, value, time, TimeUnit.MILLISECONDS);
}
@Override
public String get(String key) {
return stringRedisTemplate.opsForValue().get(key);
}
@Override
public void remove(String key) {
stringRedisTemplate.delete(key);
}
};
}
}
/**
* 內(nèi)存級別的緩存
*/
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "base.cache", havingValue = "memory", matchIfMissing = true)
public CacheManager localCacheManager() {
log.info("啟用memory緩存");
return new MemoryCacheManager();
}
}
如上述代碼:當(dāng)配置文件存在base.cache: redis
時,則初始化Redis緩存竖般,若不配置甚垦,則初始化memory
緩存。
3. @ConditionalOnClass
該注解的含義當(dāng)class存在時涣雕,加載bean艰亮。即jar里面的依賴可以不對外暴露,當(dāng)項目中引用對應(yīng)依賴時挣郭,則加載bean迄埃。
注意該類注解需要用在類上,即不允許JVM加載該類兑障。若用在方法上侄非,且項目沒有對應(yīng)class,則項目啟動時出現(xiàn)異常流译。
案例:如2中所示逞怨,可以構(gòu)造靜態(tài)內(nèi)部類實現(xiàn)。這也是為什么源碼的Config中存在大量靜態(tài)內(nèi)部類聲明bean的原因福澡。
項目實戰(zhàn):
/**
* 原生Redis的緩存
*/
@Configuration
@ConditionalOnClass(org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration.class)
@ConditionalOnProperty(name = "seal.os.cache", havingValue = "redis")
protected static class RedisCacheManagerConfiguration {
@Bean
@ConditionalOnMissingBean
public CacheManager redisCacheManager(StringRedisTemplate stringRedisTemplate) {
return new RedisCacheManager(stringRedisTemplate);
}
}
4. @ConditionalOnBean
該注解的含義是存在某個bean時骇钦,才加載這個bean。一般用來規(guī)范bean的加載順序竞漾。
5. @EnableConfigurationProperties
將配置文件加載到Configuration中眯搭。
案例:如2中所示窥翩。
6. @Import
按照順序?qū)肱渲妙?/p>
案例:
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration
@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(
RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(
RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
上述注解:@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
會按照順序?qū)隠ettuceConnectionConfiguration和JedisConnectionConfiguration配置。
LettuceConnectionConfiguration的優(yōu)先級高于JedisConnectionConfiguration鳞仙。
最終是由了LettuceConnectionConfiguration的RedisConnectionFactory寇蚊。