SpringBoot2.x常用的ConditionalXX注解
在源码阅读经常会遇到
ConditionalXX
类型的注解,它们的含义是什么?我们在开发jar时需要怎么使用?
1. @ConditionalOnMissingBean
比如我们引入了一个jar,在jar中存在一个bean,上面标有
@ConditionalOnMissingBean
。该注解的含义便是,当项目中存在相同类型的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
当配置文件中存在规则的配置时,才会加载bean。
注意点:
- @Bean的方法名字不能相同,否则@ConditionalOnProperty配置失效;
- 配置中使用枚举类型时,@ConditionalOnProperty中的值是小写;
- 当配置文件中没有对应配置项时,可以使用@ConditionalOnProperty的
matchIfMissing = true
表示,未配置时默认初始化某bean。
场景:实现了Cache,可以根据配置文件的值灵活的切换缓存。
@Getter
public enum CacheEnum {
MEMORY, REDIS;
}
配置文件:
@Data
@ConfigurationProperties("base")
public class BaseConfigProperties {
/**
* 缓存类型,默认内存级别的缓存
* 默认支持本地缓存和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);
}
};
}
}
/**
* 内存级别的缓存
*/
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "base.cache", havingValue = "memory", matchIfMissing = true)
public CacheManager localCacheManager() {
log.info("启用memory缓存");
return new MemoryCacheManager();
}
}
如上述代码:当配置文件存在base.cache: redis
时,则初始化Redis缓存,若不配置,则初始化memory
缓存。
3. @ConditionalOnClass
该注解的含义当class存在时,加载bean。即jar里面的依赖可以不对外暴露,当项目中引用对应依赖时,则加载bean。
注意该类注解需要用在类上,即不允许JVM加载该类。若用在方法上,且项目没有对应class,则项目启动时出现异常。
案例:如2中所示,可以构造静态内部类实现。这也是为什么源码的Config中存在大量静态内部类声明bean的原因。
image.png项目实战:
/**
* 原生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。一般用来规范bean的加载顺序。
5. @EnableConfigurationProperties
将配置文件加载到Configuration中。
案例:如2中所示。
6. @Import
按照顺序导入配置类
案例:
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 })
会按照顺序导入LettuceConnectionConfiguration和JedisConnectionConfiguration配置。
LettuceConnectionConfiguration的优先级高于JedisConnectionConfiguration。
最终是由了LettuceConnectionConfiguration的RedisConnectionFactory。