一文了解ConfigurationConditon 接口

简介: 在了解ConfigurationCondition 接口之前,先通过一个示例来了解一下@Conditional 和 Condition。

@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 ,会发现加载顺序第一次一致。
            </div>
目录
相关文章
|
Web App开发 JavaScript 前端开发
从零开始,轻松打造个人化Chrome浏览器插件
从零开始,轻松打造个人化Chrome浏览器插件
361 0
|
前端开发 JavaScript 定位技术
GIS前端编程-地理事件动态模拟
GIS前端编程-地理事件动态模拟
309 0
|
8月前
|
安全
一文看懂网络文化经营许可证
一文看懂网络文化经营许可证
568 3
|
存储 消息中间件 缓存
中间件redis的使用
【9月更文挑战第28天】Redis 是一个开源的、基于内存的数据结构存储系统,可用作数据库、缓存和消息中间件。它支持多种数据结构,如字符串、哈希、列表、集合和有序集合等,使其在各种应用场景中表现出色。Redis 作为缓存中间件能显著提高数据访问速度,其缓存过期策略有助于管理数据生命周期。在 .NET 应用程序中使用 Redis 缓存,可通过安装 `StackExchange.Redis` 库并连接到 Redis 服务器来实现数据的读写操作。此外,Redis 作为消息中间件,基于生产者-消费者模型实现消息队列,确保消息的可靠性和顺序性。
455 4
|
存储 安全 C++
C++ 11新特性之unique_ptr
C++ 11新特性之unique_ptr
394 4
|
人工智能 自动驾驶 安全
AI与未来生活:技术如何重塑我们的世界
在这篇文章中,我们将深入探讨人工智能(AI)如何改变我们的生活方式。从智能家居到自动驾驶汽车,从虚拟助手到医疗诊断,AI正在逐步渗透到我们生活的方方面面。我们将看到AI如何提高我们的生活效率,改善我们的生活质量,甚至帮助我们解决一些看似无法解决的问题。然而,我们也将讨论AI带来的挑战和道德问题,以及我们需要如何应对这些问题。最后,我们将展望AI在未来可能的发展趋势,以及它可能带来的更深远的影响。
|
机器学习/深度学习 算法 自动驾驶
【强化学习】什么是“强化学习”
强化学习是机器学习领域的一种重要方法,主要通过使用环境的反馈信息来指导智能体的行为,并且通过智能体收集的经验数据对自身策略进行优化。在强化学习中,我们通常用“智能体”来表示学习机器或者一个决策实体。这个智能体在某个环境中采取行动,然后收到环境的反馈信号(奖励或者惩罚),从而逐渐学习到一个最优的行动策略。在强化学习中,主要涉及到一些概念,如状态、行动、奖励、策略等等。状态指的是输入进入智能体算法的集合,行动指的是智能体做出的反应,奖励通常是指环境给予智能体的反馈信息,策略指的是智能体在某种状态下选择的行为。
536 0
【强化学习】什么是“强化学习”
|
边缘计算 搜索推荐 物联网
实时数据处理:流计算的兴起
【6月更文挑战第15天】**流计算兴起应对实时数据挑战:** 流计算是数字化时代实时数据处理的关键,它提供低延迟分析,确保数据产生时即进行处理。这种技术强调数据流的连续性,采用分布式架构实现高效、弹性且容错的数据处理。应用场景包括物联网分析、金融交易、日志监控及实时推荐系统。未来,流计算将融合AI、边缘计算,支持多源数据,并加强安全性,成为大数据处理的重要趋势。
|
弹性计算
阿里云服务器的带宽计费模式是什么意思?咋收费的?
阿里云服务器带宽计费模式分为“按固定带宽”和“按使用流量”,有什么区别?按固定带宽是指直接购买多少M带宽,比如1M、5M、10M、100M等,阿里云直接分配用户所购买的带宽值,根据带宽大小先付费再使用;按使用流量是先设置一个带宽峰值,然后根据实际公网产生的出流量来计算费用,先使用后付费。阿里云百科分享阿里云服务器“带宽计费模式”详细区别、计费及注意事项