您好,登錄后才能下訂單哦!
前言
了解SpringBoot的小伙伴對Conditional注解一定不會陌生,在SpringBoot項(xiàng)目中,Conditional注解被廣泛的使用以及擴(kuò)展出了許多Condition派生注解。雖然Conditional在SpringBoot中被豐富了很多,但它是在Spring Framework 4.0中提出的,所以本文還是以Spring Framework 為基礎(chǔ)進(jìn)行講解。
推薦閱讀
黑色的眼睛 の 個人博客
Spring Framework 組件注冊 之 FactoryBean
Spring Framework 組件注冊 之 @Import
Spring Framework 組件注冊 之 @Component
要使用@Conditional
注解,必須先了解一下Conditiona
接口,它與@Conditional
注解配合使用,通過源碼我們也可以看出,使用@Conditional
注解必須要指定實(shí)現(xiàn)Conditiona
接口的class。
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Conditional {
/**
* All {@link Condition Conditions} that must {@linkplain Condition#matches match}
* in order for the component to be registered.
*/
Class<? extends Condition>[] value();
}
在Conditiona
接口中,只定義了一個方法matches
,spring在注冊組件時,也正是根據(jù)此方法的返回值TRUE/FALSE
來決定是否將組件注冊到spring容器中
@FunctionalInterface
public interface Condition {
/**
* Determine if the condition matches.
* @param context 條件判斷的上下文環(huán)境
* @param metadata 正在檢查的類或方法的注解元數(shù)據(jù)
*/
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
在matches
中我們可以獲取到ConditionContext
接口,根據(jù)此接口對象可以獲取BeanDefinitionRegistry
,ConfigurableListableBeanFactory
等重要對象信息,根據(jù)這些對象就可以獲取和檢查spring容器初始化時所包含的所有信息,再結(jié)合業(yè)務(wù)需求,就可以實(shí)現(xiàn)組件注冊時的自定義條件判斷。
首先定義兩個普通的JavaBean類
@Data
public class Test {
private String id = "@Bean";
}
@Data
public class Test2 {
private String id = "@Conditional";
}
通過配置類和@Bean
注解,向spring容器中注冊組件
/**
* spring組件配置類
*/
@Configuration
public class TestConfiguration {
/**
* 向spring容器中注冊Test 類型下beanName為test的組件
*/
@Bean
public Test test() {
return new Test();
}
/**
* 根據(jù)TestCondition接口的條件判斷向spring容器中注冊Test2組件
*/
@Bean
@Conditional(TestCondition.class)
public Test2 test2() {
return new Test2();
}
}
自定義實(shí)現(xiàn)Condition
接口
public class TestCondition implements Condition {
/**
* 當(dāng)IOC容器中包含 Test類的bean定義信息時,條件成立
*
* @param context 條件判斷的上下文環(huán)境
* @param metadata 正在檢查的類或方法的元數(shù)據(jù)
* @return 條件是否成立
*/
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
String[] testBeanNames = beanFactory.getBeanNamesForType(Test.class);
return ArrayUtils.isNotEmpty(testBeanNames);
}
}
添加spring容器啟動引導(dǎo)類
/**
* spring 容器啟動引導(dǎo)類,測試 @Conditional 功能
*/
@ComponentScan("com.spring.study.ioc.condition")
public class TestConditionalBootstrap {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(TestConditionalBootstrap.class);
String[] names = applicationContext.getBeanNamesForType(Test.class);
System.out.println("---Test bean names : " + Arrays.asList(names));
names = applicationContext.getBeanNamesForType(Test2.class);
System.out.println("---Test2 bean names : " + Arrays.asList(names));
applicationContext.close();
}
}
運(yùn)行spring引導(dǎo)類,控制臺打印結(jié)果:
---hasTestBean : true
---Test bean names : [test]
---Test2 bean names : [test2]
由結(jié)果可以看出,Test正常注冊到了spring容器中,滿足了TestCondition
接口的條件,所有Test2 也被注冊到了spring容器中,為了進(jìn)一步驗(yàn)證結(jié)果,我們將Test組件刪除掉,僅保留Test2 的注冊,修改配置類如下
/**
* spring組件配置類,將Test組件刪除掉
*/
@Configuration
public class TestConfiguration {
/**
* 根據(jù)TestCondition接口的條件判斷向spring容器中注冊Test2組件
*/
@Bean
@Conditional(TestCondition.class)
public Test2 test2() {
return new Test2();
}
}
重新運(yùn)行spring引導(dǎo)類,控制臺打印結(jié)果如下:
---hasTestBean : false
---Test bean names : []
---Test2 bean names : []
由此可見,當(dāng)Test類在spring容器中沒有注冊時,不滿足TestCondition
接口條件,所以Test2 組件也不會被注冊到spring容器中。此時如果將test2()注冊組件上的@Conditional
組件刪除,Test2組件又會被正常注冊到spring容器中。
上面的例子中是將@Conditional
注解添加到了方法上此時條件僅對當(dāng)前方法生效,@Conditional
注解也可以加在類
上,此時條件對整個類中的組件注冊均生效。按照上面的案例,做出以下調(diào)整:
TestCondition
需要實(shí)現(xiàn)ConfigurationCondition
接口,用來對配置類做處理
當(dāng)配置類上添加了@Conditional注解時,需要注意的是,Condition接口中的條件是控制配置類本身還是控制配置類中的所有組件,因此Spring Framework提供了ConfigurationCondition接口,并使用枚舉值讓我們自定義選擇。
enum ConfigurationPhase {
/**
*Condition接口中的條件控制著配置類本身的注冊,當(dāng)條件不匹配時,不會添加@configuration類
*/
PARSE_CONFIGURATION,
/**
* 控制Condition接口中的條件是對配置類中的組件進(jìn)行解析,不會影響配置類本身的注冊
*/
REGISTER_BEAN
}
TestCondition
接口實(shí)現(xiàn)修改如下public class TestCondition implements ConfigurationCondition {
/**
* 當(dāng)IOC容器中包含 Test的bean定義信息時,條件成立
*
* @param context 條件判斷的上下文環(huán)境
* @param metadata 正在檢查的類或方法的元數(shù)據(jù)
* @return 條件是否成立
*/
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
String[] testBeanNames = beanFactory.getBeanNamesForType(Test.class);
boolean hasTestBean = ArrayUtils.isNotEmpty(testBeanNames);
System.out.println("---hasTestBean : " + hasTestBean);
return hasTestBean;
}
public ConfigurationPhase getConfigurationPhase() {
return ConfigurationPhase.REGISTER_BEAN;
}
}
@Data
@Component // 通過@Component注解直接注冊 Test 組件
public class Test {
private String id = "@Bean";
}
@Data
public class Test2 {
private String id = "Test2: @Conditional";
}
@Data
public class Test3 {
private String id = "Test3: @Conditional";
}
@Conditional
注解/**
* spring組件配置類,根據(jù)TestCondition接口的條件判斷向spring容器中注冊Test2,Test3組件
*/
@Configuration
@Conditional(TestCondition.class)
public class TestConfiguration {
@Bean
public Test3 test3() {
return new Test3();
}
@Bean
public Test2 test2() {
return new Test2();
}
}
Test3
類型的查詢String[] names = applicationContext.getBeanNamesForType(Test3.class);
System.out.println("---Test3 bean names : " + Arrays.asList(names));
啟動引導(dǎo)類,控制臺打印結(jié)果如下:
---hasTestBean : true
---Test bean names : [test]
---Test2 bean names : [test2]
---Test3 bean names : [test3]
由此可見,當(dāng)TestCondition
接口條件匹配時,Test2,Test3均被注冊到spring容器中,如果將Test組件不進(jìn)行注冊,我們看看下面的結(jié)果。
將Test
類上的@Component
注解刪除,其余代碼均不變
@Data
public class Test {
private String id = "@Bean";
}
重新啟動引導(dǎo)類,打印結(jié)果如下:
---hasTestBean : false
---Test bean names : []
---Test2 bean names : []
---Test3 bean names : []
由此可以看出,TestCondition
的條件控制著配置類中的組件注冊
@Conditional
注解加在方法上時,可以直接使用Condition
接口進(jìn)行實(shí)現(xiàn),通過條件匹配,判斷組件是否可以被注冊到spring容器中@Conditional
注解加在配置類上時,需要使用ConfigurationCondition
接口進(jìn)行實(shí)現(xiàn),通過ConfigurationPhase
來指定條件匹配對配置類本身注冊的影響。因?yàn)?code>Condition接口的條件是在spring掃描候選組件的過程中執(zhí)行的,所以在根據(jù)Bean進(jìn)行條件判斷時,需要注意此問題。如果是自定義的業(yè)務(wù)需求判斷,不會受此影響。我們平常在使用spring或者spring MVC時,@Conditional 注解的使用可能并不是很多,但是在當(dāng)下Spring Boot大行其道,并且Spring Boot對@Conditional進(jìn)行了很多的擴(kuò)展,所以了解@Conditional的使用及原理,也是對Spring Boot的基礎(chǔ)學(xué)習(xí)做更多的鋪墊。
本文對@Conditional的使用進(jìn)行了介紹,沒有深入說明Condition的原理,這些內(nèi)容將在后續(xù)的spring組件掃描過程中進(jìn)行說明。
學(xué)習(xí)永遠(yuǎn)都不是一件簡單的事情,可以有迷茫,可以懶惰,但是前進(jìn)的腳步永遠(yuǎn)都不能停止。
不積跬步,無以至千里;不積小流,無以成江海;
黑色的眼睛 の 個人博客
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。