一文深入理解ConfigurationConditon 介面
一文了解ConfigurationConditon 介面
ConfigurationCondition 介面說明
@Conditional 和 Condition
在瞭解ConfigurationCondition 介面之前,先通過一個示例來了解一下@Conditional 和 Condition。
- 首先新建一個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 **,會發現載入順序第一次一致。
相關文章
- 深入理解Callable介面
- 一文深入理解快照技術
- 介面測試之深入理解HTTPSHTTP
- 深入理解 PHP 的 7 個預定義介面PHP
- 深入理解ES6--12.代理與反射介面反射
- 深入理解介面隔離原則:構建靈活的面向介面軟體
- 深入理解margin
- 深入理解ReactReact
- 深入理解KVO
- 深入理解 ReentrantLockReentrantLock
- 深入理解 PWA
- 深入理解BFC
- 深入理解volatile
- 深入理解MVCMVC
- 深入理解 TypeScriptTypeScript
- 深入理解JSCoreJS
- 深入理解JavaScriptCoreJavaScript
- 深入理解Isolate
- 深入理解 JVMJVM
- 深入理解HashMapHashMap
- 深入理解ThreadLocalthread
- 深入理解TransformORM
- 深入理解 GitGit
- 深入理解reduxRedux
- BFC深入理解
- 深入理解paddingpadding
- 深入理解JSXJS
- 深入理解 SynchronizationContextContext
- 深入理解JVMJVM
- 深入理解AQSAQS
- RandomAccess介面理解randomMac
- 【Interview】深入理解ReentrantLockViewReentrantLock
- 深入理解Java PriorityQueueJava
- 深入理解 Java 方法Java
- 深入理解JavaScript原型JavaScript原型
- 深入淺出理解ReduxRedux
- 深入理解AbstractQueuedSynchronizer(AQS)AQS
- Java:IO:深入理解Java