ConfigurationCondition 接口说明
@Conditional 和 Condition
? 在了解 ConfigurationCondition 接口之前, 先通过一个示例来了解一下 @Conditional 和 Condition.(你也可以通过 https://www.cnblogs.com/cxuanBlog/p/10960575.html 详细了解)
首先新建一个 Maven 项目 (可以使用 SpringBoot 快速搭建), 添加 Spring4.0 的 pom.xml 依赖
- <?xml version="1.0" encoding="UTF-8"?>
- <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-parent</artifactId>
- <version>2.1.5.RELEASE</version>
- <relativePath/> <!-- lookup parent from repository -->
- </parent>
- <groupId>com.cxuan.configuration</groupId>
- <artifactId>configuration-condition</artifactId>
- <version>0.0.1-SNAPSHOT</version>
- <name>configuration-condition</name>
- <description>Demo project for Spring Boot</description>
- <properties>
- <java.version>1.8</java.version>
- <spring.version>4.3.13.RELEASE</spring.version>
- </properties>
- <dependencies>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-context</artifactId>
- <version>${spring.version}</version>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-beans</artifactId>
- <version>${spring.version}</version>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-core</artifactId>
- <version>${spring.version}</version>
- </dependency>
- </dependencies>
- <build>
- <plugins>
- <plugin>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-maven-plugin</artifactId>
- </plugin>
- </plugins>
- </build>
- </project>
新建一个
IfBeanAExistsCondition
类, 该类继承了 Condition 接口, 提供某些注册条件的逻辑
- public class IfBeanAExistsCondition implements Condition {
- @Override
- public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
- boolean IfContainsbeanA = context.getBeanFactory().containsBeanDefinition("beanA");
- return IfContainsbeanA;
- }
- }
Condition 是一个接口, 里面只有一个方法就是 matches, 上述表明如果 ConditionContext 的 beanFactory 包括名称为 beanA 的 bean 就返回 true, 否则返回 false 不进行注册.
为了测试 Condition 是否可用, 我们新建一个
ConfigurationConditionApplication
类, 注册两个 Bean 分别为 BeanA 和 BeanB,BeanB 的注册条件是 BeanA 首先进行注册, 采用手动注册和刷新的方式. 详见 https://www.cnblogs.com/cxuanBlog/p/10958307.html, 具体代码如下:
- public class ConfigurationConditionApplication {
- private static void loadContextAndVerifyBeans(Class...classToRegistry){
- AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
- context.register(classToRegistry);
- context.refresh();
- System.out.println("Has BeanA?" + context.containsBean("beanA"));
- System.out.println("Has BeanB?" + context.containsBean("beanB"));
- }
- public static void main(String[] args) {
- loadContextAndVerifyBeans(BeanA.class);
- loadContextAndVerifyBeans(BeanA.class,BeanB.class);
- loadContextAndVerifyBeans(BeanB.class);
- loadContextAndVerifyBeans(BeanB.class,BeanA.class);
- }
- }
- @Configuration()
- class BeanA{}
- @Conditional(IfBeanAExistsCondition.class)
- @Configuration()
- class BeanB{}
输出结果:
- ...
- Has BeanA? true
- Has BeanB? false
- ...
- Has BeanA? true
- Has BeanB? true
- ...
- Has BeanA? false
- Has BeanB? false
- ...
- Has BeanA? true
- Has BeanB? false
来解释一下上面的输出结果, 第一次只注册了一个 BeanA 的 bean,@Configuration 标注的 BeanA 默认注册的 definitionName 为 beanA, 首字母小写.
第二次同时传入了 BeanA.class 和 BeanB.class, 由于 BeanB 的注解上标明 @Conditional(IfBeanAExistsCondition.class) 表示的是注册 BeanA 之后才会注册 BeanB, 所以注册了 beanA, 因为 beanA 被注册了, 所以同时也就注册了 beanB.
第三次只传入了 BeanB.class, 因为没有注册 BeanA 和 BeanB, 所以两次输出都是 false.
第四次先传入了 BeanB.class, 后又传入了 BeanA.class, 根据加载顺序来看, BeanB.class 首先被加载, 然后是 BeanA.class 被加载, BeanB 被加载的时候 BeanA.class 还没有被注入, 之后 BeanA 才会注入, 所以输出的结果是 true 和 false.
上述例子可以把 BeanA 和 BeanB 类放入 ConfigurationConditionApplication 中, 类似
- public class ConfigurationConditionApplication {
- @Configuration()
- static class BeanA{
- }
- @Conditional(IfBeanAExistsCondition.class)
- @Configuration()
- static class BeanB{
- }
- }
但是需要把 BeanA 和 BeanB 定义为静态类, 因为静态类与外部类无关能够独立存在, 如果定义为非静态的, 启动会报错.
关于 ConfigurationConditon
? ConfigurationCondition 接口是 Spring4.0 提供的注解. 位于 org.springframework.context.annotation 包内, 继承于 Condition 接口. Condition 接口和 @Configuration 以及 @Conditional 接口为 bean 的注册提供更细粒度的控制, 允许某些 Condition 在匹配时根据配置阶段进行调整.
- public interface ConfigurationCondition extends Condition {
- // 评估 condition 返回的 ConfigurationPhase
- ConfigurationPhase getConfigurationPhase();
- // 可以评估 condition 的各种配置阶段.
- enum ConfigurationPhase {
- // @Condition 应该被评估为正在解析 @Configuration 类
- // 如果此时条件不匹配, 则不会添加 @Configuration 类.
- PARSE_CONFIGURATION,
- // 添加常规 (非 @Configuration)bean 时, 应评估 @Condition.Condition 将不会阻止 @Configuration 类
- // 添加. 在评估条件时, 将解析所有 @Configuration
- REGISTER_BEAN
- }
- }
getConfigurationPhase() 方法返回 ConfigurationPhase 的枚举. 枚举类内定义了两个 enum,PARSE_CONFIGURATION 和 REGISTER_BEAN, 表示不同的注册阶段.
? 我们现在对 condition 实现更细粒度的控制, 实现了 ConfigurationCondition 接口, 我们现在需要实现 getConfigurationPhase() 方法获得 condition 需要评估的阶段.
新建
IfBeanAExistsConfigurationCondition
类, 实现了 ConfigurationCondition 接口, 分别返回 ConfigurationPhase.REGISTER_BEAN 和 ConfigurationPhase.PARSE_CONFIGURATION 阶段.
- public class IfBeanAExistsConfigurationCondition implements ConfigurationCondition {
- @Override
- public ConfigurationPhase getConfigurationPhase() {
- return ConfigurationPhase.REGISTER_BEAN;
- }
- // @Override
- // public ConfigurationPhase getConfigurationPhase() {
- // return ConfigurationPhase.PARSE_CONFIGURATION;
- // }
- @Override
- public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
- return context.getBeanFactory().containsBeanDefinition("beanA");
- }
- }
新建
SpringConfigurationConditionExample
类, 与上述测试类基本相同, 就是把 @Conditional 换为了 @Conditional(IfBeanAExistsConfigurationCondition.class)
测试类启动, 输出结果
- ...
- Has BeanA? true
- Has BeanB? false
- ...
- Has BeanA? true
- Has BeanB? true
- ...
- Has BeanA? false
- Has BeanB? false
- ...
- Has BeanA? true
- Has BeanB? true
也就是说, 如果返回的是 PARSE_CONFIGURATION 阶段的话, 不会阻止 @Configuration 的标记类的注册顺序, 啥意思呢?
第一个结果, 只注册了 BeanA, 因为只有 BeanA 加载.
第二个结果, 注册了 BeanA 和 BeanB, 因为 BeanA 和 BeanB 都被加载
第三个结果, 因为 BeanB 注册的条件是 BeanA 注册, 因为 BeanA 没有注册, 所以 BeanB 不会注册
第四个结果, 不论 BeanA 和 BeanB 的加载顺序如何, 都会直接进行注册.
如果把 REGISTER_BEAN 改为 PARSE_CONFIGURATION , 会发现加载顺序第一次一致.
来源: http://www.bubuko.com/infodetail-3079936.html