一文了解ConfigurationConditon介面
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 ,會發現載入順序第一次一致。