文章目录
前言
有一篇文章:《Spring Boot @Condition 注解,组合条件你知道吗》十分值得阅读
一、@Conditional的作用
1. @Conditional
@Conditional注解可以作用在创建Bean的类或者方法上,给Spring容器是否加载此bean,添加额外的约束或者判断。
作用在:
- 类注解(@Component、@Controller、@Service、@Repository)
- 方法注解(@Bean)
的bean都可以用@Conditional做额外约束
当@Conditional注解的判断条件不满足时,对应的Bean不会生成。
1. Condition接口
根据上面的介绍,我们知道@Conditional注解是来让Spring容器选择是否加载对应bean的。也就是Spring可以通过**“某种判断”,来确定结果是true或false,进而决定是否加载此bean。
在具体的实现上,“这个判断”**是通过程序员实现Condition
接口并重写public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata)
方法来实现的。
二、使用示例
1. 编写Condition接口的实现
编写一个我们自己的Condition
接口的实现类:ConditionMatchService
。这个实现类需要复写返回结果为布尔值的boolean match()
方法。在match()方法中,我们编写判断条件:当环境为"condition"字符串时,返回为true;其他为false
/**
* @Author: Albert Guo
*/
public class ConditionMatchService implements Condition {
public static final String PROFILE = "condition";
/**
* 当此方法返回true时,注解生效
* @param context
* @param metadata
* @return
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String[] activeProfiles = environment.getActiveProfiles();
if(ArrayUtils.isNotEmpty(activeProfiles)){
for (int i = 0; i < activeProfiles.length; i++) {
//当环境为condition时,返回true
if (PROFILE.equals(activeProfiles[i])) {
return true;
}
}
}
return false;
}
}
2.添加@Conditional注解
在创建bean的地方添加@Conditional注解条件,并将刚编写的ConditionMatchService
类作为判断条件添加
/**
* @Author: Albert Guo
*/
@Configuration
public class ConditionConfiguration {
/**
* 如果ConditionMatchService的match()方法返回true,则ConditionDTO加载,会打印beanName
* @return
*/
@Bean(initMethod="init")
@Conditional(ConditionMatchService.class)
public ConditionDTO conditionDTO() {
return new ConditionDTO();
}
}
其中ConditionDTO
内容如下,加入了必要的日志:
/**
* @Author: Albert Guo
*/
@Slf4j
@Data
public class ConditionDTO implements BeanNameAware {
//bean的名称
private String beanName;
public void init() {
log.info("ConditionDTO 开始加载,beanName=={}", beanName);
}
@Override
public void setBeanName(String name) {
beanName = name;
}
}
3. 编写Test测试类
/**
* 测试{@code @Conditional}注解
* @Author: Albert Guo
*/
@Slf4j
@ActiveProfiles("dev")
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {ConditionConfiguration.class})
public class ConditionTest implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Test
public void test() {
if(applicationContext.containsBean("conditionDTO")) {
log.info("{}的bean存在!", ConditionMatchService.PROFILE);
} else {
log.warn("{}的bean不存在!", ConditionMatchService.PROFILE);
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
4. 测试结果
- 当Profile=dev时,可以看到bean并没有加载
- 当Profile=condition时,可以看到bean加载成功
三. @ConditionalOnXXX
从上面的例子可以看到,@Condition一般要和Condition接口配合使用,还需要重写match()方法,开发起来并不是很精简。有时候编写框架时,设计师更在乎是否存在某个Bean、是否某个包或资源被引入、是否处于某种特殊环境下…对于这种存在与否的判断,可以抽象出某种共性,而不用再底层的每次重写match方法来逐个判断。
因此SpringBoot基于@Condition接口,进一步封装了@ConditionalOnXXX注解。对于这些注解,开发者可以不用再实现Condition接口,而是直接在@ConditionalOnXXX注解的value值中,写入判断的条件(例如class名称、path路径、properties配置等),进一步减少了开发量。
常用到的注解有:
@ConditionalOnBean**(仅仅在当前上下文中存在某个对象时,才会实例化一个Bean)
@ConditionalOnClass(某个class位于类路径上,才会实例化一个Bean)
@ConditionalOnExpression(当表达式为true的时候,才会实例化一个Bean)
@ConditionalOnMissingBean(仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean)
@ConditionalOnMissingClass(某个class类路径上不存在的时候,才会实例化一个Bean)
@ConditionalOnNotWebApplication(不是web应用)
具体的用法可以参考前言中的文章