Spring Boot-22-@Conditional注解使用

在我们 Spring Boot 项目中,很多地方都用到了 @Conditional 注解用来指定一些条件, 这个注解的作用是根据是否满足某一个特定的条件来决定是否创建某个 Bean ,这个注解的存在也是 Spring Boot 实现自动配置的关键基础能力,比如我们在 web 工厂类加载的部分就遇到过这个注解

常见的 @Conditional 注解

注解 作用
@ConditionalOnBean 当某个Bean存在的情况下
@ConditionalOnMissingBean 当某个Bean不存在的情况下
@ConditionalOnClass 当某个类存在的情况下
@ConditionalOnMissingClass 当某个类不存在的情况下
@ConditionalOnWebApplication 处于web环境下
@ConditionalOnProperty 当某个 property 存在的时候
@ConditionalOnNotWebApplication 不处于web环境的情况下
@ConditionalOnJava 在特定的Java版本下

动手实践

了解了 @Condition 注解的作用之后,我们来写一个类实际使用测试一下

首先新建一个类, 如下

1
2
3
4
@Component
@ConditionalOnProperty("com.test")
public class ConditionTest {
}

这里首先是 @Component 注解, 表示要注入到容器中,然后用到了 @ConditionalOnProperty 注解,表示 com.test 这个属性要在环境变量中存在

然后写个测试类看一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@SpringBootTest
public class ConditionalTests implements ApplicationContextAware {

private ApplicationContext applicationContext;

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}

@Test
public void test1(){
System.out.println(applicationContext.getBean(ConditionTest.class));
}


}

这里直接运行, 从容器中找这个类, 看一下输出结果

image

这里可以看到,我们在容器中是找不到这个类的,因为在环境变量中并没有 com.test 这个属性

然后我们在 application 配置文件中配置一下这个属性

1
2
com:
test: test

再次运行,看一下输出结果

image

这次就成功注入到容器中了

原理分析

来看一下上面用的 @ConditionOnProperty 注解

image

这里其实用了一个 @Condition 注解, 点进去

image

这个注解有一个属性,是一个类,这个类必须是 Condition 的子类,进入这个类看一下

image

可以看到这个接口只有一个方法 matches() 这个方法返回是 true 的话表示符合条件,就会被注入到容器中,如果是 false 则不会被注入到容器中

我们这里可以看一下他这个注解是怎么判断的

@ConditionOnProperty 这个注解用的是 @Conditional(OnPropertyCondition.class) 进入 OnPropertyCondition 这个类, 这个类是继承自 SpringBootCondition 这个抽象类, 所以我们先看一下 SpringBootCondition.matches 这个方法

image

这里可以看到重点的判断方法还是 getMatchOutcome() 这个方法,这个方法是具体子类实现的, 然后回到 OnPropertyCondition 看一下这个方法

image

这里的判断是在 determineOutcome() 这个方法当中,点进去

image

这里首先做了一个包装,然后调用它的 collectProperties() 方法做判断

image

这里就是调用环境变量,然后做具体的验证

经过上面的分析,我们是不是可以仿着这个,自己实现一个 @Condition 注解

自定义实现 @Condition 注解

首先我们需要一个类,实现 Condition 然后重写他的 matches() 方法,代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class MyCondition implements Condition {

@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 获取对应注解的属性的值
String[] values = (String[]) metadata.getAnnotationAttributes("com.zhou.springboot.example.conditional.MyConditionAnnotation")
.get("value");

for (String value : values) {
// 从环境变量中判断,有没有这个属性, 没有的话 return false
if (StringUtils.isEmpty(context.getEnvironment().getProperty(value))) {
return false;
}
}

return true;
}

}

然后就需要创建一个注解

1
2
3
4
5
6
7
8
9
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(MyCondition.class)
public @interface MyConditionAnnotation {

String[] value() default {};

}

这里加上 @Conditional 注解,指定我们刚刚创建的类,然后就可以在之前的 ConditionTest 类中使用测试了

1
2
3
4
5
@Component
@MyConditionAnnotation({"com.test1", "com.test2"})
public class ConditionTest {

}

这里整体作用就是 com.test1com.test2 这两个属性都有值的情况下,这个类才会被注入到容器中

总结

关于 @Condition 注解的使用就介绍到这里,下面做一个总结

作用:

  • 通过 @Condition 注解可以实现根据是否满足某一个特定的条件来决定是否创建某个 Bean

自定义 @Condition 注解的流程

  1. 实现一个自定义注解并且引入 @Condition 注解
  2. 实现 Condition 接口,并且重写 matches() 方法,符合条件就返回 true
  3. 自定义注解引入上面创建的 Condition 接口的实现