一文了解ConfigurationConditon接口

ConfigurationCondition 接口說明

@Conditional 和 Condition

​ 在瞭解ConfigurationCondition 接口以前,先經過一個示例來了解一下@Conditional 和 Condition。(你也能夠經過 https://www.cnblogs.com/cxuanBlog/p/10960575.html 詳細瞭解)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不進行註冊。java

  • 爲了測試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{}

輸出結果:spring

...
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,首字母小寫。apache

第二次同時傳入了BeanA.class 和 BeanB.class, 因爲BeanB的註解上標明@Conditional(IfBeanAExistsCondition.class)表示的是註冊BeanA以後纔會註冊BeanB,因此註冊了beanA,由於beanA被註冊了,因此同時也就註冊了beanB。maven

第三次只傳入了BeanB.class,由於沒有註冊BeanA和BeanB,因此兩次輸出都是false。ide

第四次先傳入了BeanB.class,後又傳入了BeanA.class,根據加載順序來看,BeanB.class 首先被加載,而後是BeanA.class 被加載,BeanB被加載的時候BeanA.class 尚未被注入,以後BeanA纔會注入,因此輸出的結果是true和false。spring-boot

上述例子能夠把BeanA和BeanB類放入ConfigurationConditionApplication中,相似測試

public class ConfigurationConditionApplication {
   
@Configuration()
static class BeanA{}

@Conditional(IfBeanAExistsCondition.class)
@Configuration()
static class BeanB{}
  
}

可是須要把BeanA和BeanB定義爲靜態類,由於靜態類與外部類無關可以獨立存在,若是定義爲非靜態的,啓動會報錯。ui

關於ConfigurationConditon

​ ConfigurationCondition接口是Spring4.0提供的註解。位於org.springframework.context.annotation包內,繼承於Condition接口。Condition接口和@Configuration以及@Conditional接口爲bean的註冊提供更細粒度的控制,容許某些Condition在匹配時根據配置階段進行調整。code

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 ,會發現加載順序第一次一致。
相關文章
相關標籤/搜索