springboot的@ConditionalOnBean註解

良工說技術 發表於 2022-07-02
Spring

  上篇文章中分析了springboot的自動注入的原理,可在文章後面的推薦閱讀中溫習哦。在自動注入的原理那篇文章中提到了@ConditionalOnXX註解,今天來看下springboot中的@ConditionalOnXX註解,該註解表示的是一類註解。馬上開始吧。

一、@ConditionalOnXX註解初識

  @ConditionalOnXX註解被定義在了spring-boot-autoconfigure包中,有以下幾個,

springboot的@ConditionalOnBean註解

  從上圖中可以看到經常碰到的@ConditionalOnBean、@ConditionalOnClass、@ConditionalOnMissingBean、@ConditionalOnMissingClass、@ConditionalOnProperty、@ConditionalOnResource、@ConditionalOnSingleCandidate等。這些註解均在”org.springframework.boot.autoconfigure.condition“包下,感興趣的小夥伴可以自行檢視原始碼。

二、深入@ConditionalOnBean註解

  上面提到了多個@ConditionalOnXX註解,下面逐一對這些常見的註解進行講解。有意思的是,這些註解很多都是成對出現的,而且意思都是相近的。今天先來看下@CondtionalOnBean註解

2.1、@ConditionalOnBean

  @ConditionalOnBean註解的定義如下,

springboot的@ConditionalOnBean註解

  可以明確的一點是@ConditionalOnBean可以用在類/方法上。可以配置的屬性有下面這些,

springboot的@ConditionalOnBean註解

  平時用的比較多的有value、type、name三個,這三個可以看到都是陣列,也就是說可以配置多個。

  上面提到@ConditionalOnBean可以配置在方法上,是所有的方法都可以嗎?

2.2、@ConditionalOnBean如何標識方法

  @ConditionalOnBean標識在方法上,可以標識在所有的方法上嗎,這個我們要從該註解的註釋上去找答案了。

springboot的@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");
    }
}

 看下啟動日誌是否會列印構造方法中的日誌,

springboot的@ConditionalOnBean註解

可以看到正常列印了,也就是說ClassA和ClassB均被注入到了容器中,這是使用@ConditionalOnBean(value=)的情況,下面看使用@ConditionalOnBean(type=)的情況,

2.2.2、@ConditionalOnBean(type=)

這裡的type要求填入的是類的全路徑,比如com.my.template.config.ClassA

把配置類中修改為下面的樣子,

springboot的@ConditionalOnBean註解

再次啟動觀察日誌,

springboot的@ConditionalOnBean註解

從日誌中可以看到依舊是可以的,下面我把MyAutoConfig類中的classA()方法,放到classB()方法下面,

springboot的@ConditionalOnBean註解

再執行看日誌,

springboot的@ConditionalOnBean註解

可以看到沒有執行ClassB的構造方法,也就是classB()方法沒執行。可以得出:在配置類中的@Bean標識的方法是有順序的,前邊的會先解析,後邊的後解析,後面的要引用前面的是引用不到的,反之則可以。

這種方式下,沒有其他方式可以執行classB()方法嗎,有的,使用@ConditionalOnClass(value={ClassA.class}),感興趣的小夥伴可以自己試試哦。

2.2.3、@ConditionalOnBean(name=)

下面看使用name屬性的情況,

MyAutoConfig.java修改成下面的樣子,

springboot的@ConditionalOnBean註解

啟動日誌如下,

springboot的@ConditionalOnBean註解

正常啟動,且初始化了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");
    }
}

啟動日誌如下,

springboot的@ConditionalOnBean註解

可以看到例項化了MyAutoConfig2,但是MyAutoConfig確沒有,這是因為其類上有@ConditionalOnBean(type = {"com.my.template.config.ClassA"})註解,且在解析MyAutoConfig時並未解析ClassA,把該註解換成@ConditionalOnBean(value= {ClassA.class})看看可以嗎

springboot的@ConditionalOnBean註解

可以看到還是不行,那麼換成@ConditionalOnClass(value={ClassA.class})

springboot的@ConditionalOnBean註解

完美解決問題。

三、總結

  本文主要分析了@ConditionalOnBean註解的使用場景,

  1、該註解的作用時機是在生成bean的時候,確切的說是在解析beanDefinition的時候

  2、該註解可以用在配置類和標識有@Bean的方法上;

  3、三個常用屬性value、name、type均有解析順序的問題;

  4、value、name、type各自的配置方式

 

本次分享就到這裡了,下次,我們@ConditionalOnClass註解見。

推薦閱讀

深入理解springboot的自動注入

我的第一個springboot  starter

springboot的@ConditionalOnBean註解