【每天学点Spring】@ConditionalOnProper
@ConditionalOnProperty是Springboot的注解。是spring-boot-autoconfigure包里的注解,从Spring boot v1.1.0就开始就了。
当我们创建bean的时候,有时候需要根据不同的property的值来创建不同的bean,比如property值=dev或者prod。这时候就需要使用@ConditionalOnProperty注解了,它可以在某些环境变量存在的时候并且等于特定值的时候,才去创建某个bean。
@ConditionalOnProperty有5个变量,分别是value, prefix, name, havingValue和matchIfMissing:
ConditionalOnProperty.jpg
1. 简单demo
@Service(value = "simpleCacheService")
@ConditionalOnProperty(prefix = "conditional.cache", name = "type")
public class SimpleCacheServiceImpl implements CacheService {
public void init() {}
}
在application.properties里配置:
conditional.cache.type=simple
测试SimpleCacheServiceImpl有没有被加载:
@SpringBootTest
public class SimpleCacheServiceTest {
@Autowired
private CacheService simpleCacheService;
@Test
public void initTest() {
Assertions.assertTrue(simpleCacheService.getClass().getName().contains("SimpleCacheService"));
}
}
如果不配置conditional.cache.type,那么simpleCacheService注入不了。
上述@ConditionalOnProperty(prefix = "conditional.cache", name = "type")配置的意思是当有profix.name配置存在的时候,即可加载该Bean。
2. 高级用法
@Service(value = "redisCacheService")
@ConditionalOnProperty(prefix = "conditional.cache", value = "type", havingValue = "redis")
public class RedisCacheServiceImpl implements CacheService {
public void init() {}
}
测试通过:
@SpringBootTest
public class RedisCacheServiceTest {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner();
@Test
public void initTest() {
this.contextRunner.withPropertyValues("conditional.cache.type=redis")
.withUserConfiguration(RedisCacheServiceImpl.class)
.run(context -> {
CacheService redisCacheService = (CacheService) context.getBean("redisCacheService");
Assertions.assertTrue(redisCacheService.getClass().getName().contains("RedisCacheService"));
});
}
}
加了havingValue="redis",意味着有如下配置并且值等于redis时,才会加载redisCacheService:
conditional.cache.type=redis
3. Spring源码中的@ConditionalOnProperty
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(WebMvcConfigurer.class)
@ConditionalOnMissingBean({ OpenEntityManagerInViewInterceptor.class, OpenEntityManagerInViewFilter.class })
@ConditionalOnMissingFilterBean(OpenEntityManagerInViewFilter.class)
@ConditionalOnProperty(prefix = "spring.jpa", name = "open-in-view", havingValue = "true", matchIfMissing = true)
protected static class JpaWebConfiguration {
// 略
}
先别管别的Conditional注解,看最后一个:
matchIfMissing=true,意思是,不配置spring.jpa.open-in-view也会加载Bean,但如果有配置,那么需要配置:spring.jpa.open-in-view=true
反过来说,如何不加载这个Bean:spring.jpa.open-in-view=false
4. 最后放下源码:
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnPropertyCondition.class)
public @interface ConditionalOnProperty {
// 同name
String[] value() default {};
// 前缀,如:acme.system.feature
String prefix() default "";
// 与prefix协同使用:
String[] name() default {};
// property里应该配的值,如果相等,则表示激活该bean
String havingValue() default "";
// 默认不配置就不激活该bean
boolean matchIfMissing() default false;
}
【参考】
Spring conditionalOnProperty: https://www.baeldung.com/spring-conditionalonproperty
Spring Boot ApplicationContextRunner 测试指南: https://blog.csdn.net/j3T9Z7H/article/details/94365999