在瞭解ConfigurationCondition 接口以前,先經過一個示例來了解一下@Conditional 和 Condition。(你也能夠經過 https://www.cnblogs.com/cxuanBlog/p/10960575.html 詳細瞭解)html
<?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
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
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的加載順序如何,都會直接進行註冊。