上篇文章中分析了springboot的自動注入的原理,可在文章後面的推薦閱讀中溫習哦。在自動注入的原理那篇文章中提到了@ConditionalOnXX註解,今天來看下springboot中的@ConditionalOnXX註解,該註解表示的是一類註解。馬上開始吧。
一、@ConditionalOnXX註解初識
@ConditionalOnXX註解被定義在了spring-boot-autoconfigure包中,有以下幾個,
從上圖中可以看到經常碰到的@ConditionalOnBean、@ConditionalOnClass、@ConditionalOnMissingBean、@ConditionalOnMissingClass、@ConditionalOnProperty、@ConditionalOnResource、@ConditionalOnSingleCandidate等。這些註解均在”org.springframework.boot.autoconfigure.condition“包下,感興趣的小夥伴可以自行檢視原始碼。
二、深入@ConditionalOnBean註解
上面提到了多個@ConditionalOnXX註解,下面逐一對這些常見的註解進行講解。有意思的是,這些註解很多都是成對出現的,而且意思都是相近的。今天先來看下@CondtionalOnBean註解
2.1、@ConditionalOnBean
@ConditionalOnBean註解的定義如下,
可以明確的一點是@ConditionalOnBean可以用在類/方法上。可以配置的屬性有下面這些,
平時用的比較多的有value、type、name三個,這三個可以看到都是陣列,也就是說可以配置多個。
上面提到@ConditionalOnBean可以配置在方法上,是所有的方法都可以嗎?
2.2、@ConditionalOnBean如何標識方法
@ConditionalOnBean標識在方法上,可以標識在所有的方法上嗎,這個我們要從該註解的註釋上去找答案了。
從上面的註釋可以知道,@ConditionalOnBean註解使用在@Bean標識的方法上,都知道@Bean註解的作用是向容器中注入一個bean,也就是@ConditionalOnBean作用的時機是在生成bean的時候。再看註釋“the bean class defaults to return type of the factory method”,大體意思是預設返回的bean是工廠方法的型別,這個不好理解,通過一個例子看下。
2.2.1、@ConditionalOnBean(value=)
MyAutoConfig.java
package com.my.template.config;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 配置類
* @date 2022/7/2 15:02
*/
@Configuration
public class MyAutoConfig {
@Bean(value = "classA")
public ClassA classA(){
return new ClassA();
}
@Bean
@ConditionalOnBean(value = {ClassA.class})
public ClassB classB(){
return new ClassB();
}
}
在上面的配置類中,在@Bean註解的方法上使用了@ConditionalOnBean註解,註解中使用的value屬性,代表的是隻要存在ClassA這個類邊會執行classB()方法,下面看ClassA和ClassB都很簡單,就是兩個普通的類,
ClassA.java和ClassB.java
package com.my.template.config;
/**
* @date 2022/7/2 15:03
*/
public class ClassA {
public ClassA() {
System.out.println("constructor classA");
}
}
package com.my.template.config;
/**
* @date 2022/7/2 15:04
*/
public class ClassB {
public ClassB() {
System.out.println("constructor classB");
}
}
看下啟動日誌是否會列印構造方法中的日誌,
可以看到正常列印了,也就是說ClassA和ClassB均被注入到了容器中,這是使用@ConditionalOnBean(value=)的情況,下面看使用@ConditionalOnBean(type=)的情況,
2.2.2、@ConditionalOnBean(type=)
這裡的type要求填入的是類的全路徑,比如com.my.template.config.ClassA
把配置類中修改為下面的樣子,
再次啟動觀察日誌,
從日誌中可以看到依舊是可以的,下面我把MyAutoConfig類中的classA()方法,放到classB()方法下面,
再執行看日誌,
可以看到沒有執行ClassB的構造方法,也就是classB()方法沒執行。可以得出:在配置類中的@Bean標識的方法是有順序的,前邊的會先解析,後邊的後解析,後面的要引用前面的是引用不到的,反之則可以。
這種方式下,沒有其他方式可以執行classB()方法嗎,有的,使用@ConditionalOnClass(value={ClassA.class}),感興趣的小夥伴可以自己試試哦。
2.2.3、@ConditionalOnBean(name=)
下面看使用name屬性的情況,
MyAutoConfig.java修改成下面的樣子,
啟動日誌如下,
正常啟動,且初始化了ClassB類。
2.3、ConditionalOnBean標識類
這裡說的標識類,我們一般都預設為標識配置類,即帶有@Configuration註解的類。這裡同時會有value、type、name三種不同的屬性配置,需要注意的是value配置的是Class物件,標識的是隻要在解析過程中載入了該類即可。type配置的是全類名,name配置的是bean的名稱,type和name的配置要求的是需要解析了該BeanDefinition,同時和順序是有關係的。演示下面的例子
MyAutoConfig.java
package com.my.template.config;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 自動配置類
* @date 2022/7/2 15:02
*/
@Configuration
@ConditionalOnBean(type = {"com.my.template.config.ClassA"})
public class MyAutoConfig {
public MyAutoConfig(){
System.out.println("constructor MyAutoConfig");
}
}
MyAutoConfig2.java
package com.my.template.config;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 自動配置類
*
* @author wangcj5
* @date 2022/7/2 15:02
*/
@Configuration
public class MyAutoConfig2 {
@Bean
public ClassA classA(){
return new ClassA();
}
public MyAutoConfig2(){
System.out.println("constructor MyAutoConfig2");
}
}
啟動日誌如下,
可以看到例項化了MyAutoConfig2,但是MyAutoConfig確沒有,這是因為其類上有@ConditionalOnBean(type = {"com.my.template.config.ClassA"})註解,且在解析MyAutoConfig時並未解析ClassA,把該註解換成@ConditionalOnBean(value= {ClassA.class})看看可以嗎
可以看到還是不行,那麼換成@ConditionalOnClass(value={ClassA.class})
完美解決問題。
三、總結
本文主要分析了@ConditionalOnBean註解的使用場景,
1、該註解的作用時機是在生成bean的時候,確切的說是在解析beanDefinition的時候
2、該註解可以用在配置類和標識有@Bean的方法上;
3、三個常用屬性value、name、type均有解析順序的問題;
4、value、name、type各自的配置方式
本次分享就到這裡了,下次,我們@ConditionalOnClass註解見。
推薦閱讀