通過條件來控制bean的註冊

@Conditional註解

可以通過@Conditional來控制bean是否需要註冊,控制被@Configuration標註的配置類是否需要被解析等。

Condition接口
是一個函數式接口,內部只有一個matches方法,用來判斷條件是否成立的,2個參數:

  • context:條件上下文,ConditionContext接口類型的,可以用來獲取容器中的bean信息
  • metadata:用來獲取被@Conditional標註的對象上的所有註解信息

Spring對配置類的處理主要分爲2個階段:

  1. 配置類解析階段
    會得到一批配置類的信息,和一些需要註冊的bean
  2. bean註冊階段
    將配置類解析階段得到的配置類和需要註冊的bean註冊到spring容器中

什麼是配置類
類中有下面任意註解之一的就屬於配置類:

  • 類上有@Compontent註解
  • 類上有@Configuration註解
  • 類上有@CompontentScan註解
  • 類上有@Import註解
  • 類上有@ImportResource註解
  • 類中有@Bean標註的方法

判斷一個類是不是一個配置類,是否的是下面這個方法,可以看一下:org.springframework.context.annotation.ConfigurationClassUtils#isConfigurationCandidate

Spring對配置類處理過程
源碼位置org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions

整個過程大致的過程如下:

  1. 通常我們會通過new AnnotationConfigApplicationContext()傳入多個配置類來啓動spring容器
  2. spring對傳入的多個配置類進行解析
  3. 配置類解析階段:這個過程就是處理配置類上面6中註解的過程,此過程中又會發現很多新的配置類,比如@Import導入的一批新的類剛好也符合配置類,而被@CompontentScan掃描到的一些類剛好也是配置類;此時會對這些新產生的配置類進行同樣的過程解析
  4. bean註冊階段:配置類解析後,會得到一批配置類和一批需要註冊的bean,此時spring容器會將這批配置類作爲bean註冊到spring容器,同樣也會將這批需要註冊的bean註冊到spring容器
  5. 經過上面第3個階段之後,spring容器中會註冊很多新的bean,這些新的bean中可能又有很多新的配置類
  6. Spring從容器中將所有bean拿出來,遍歷一下,會過濾得到一批未處理的新的配置類,繼續交給第3步進行處理
  7. step3到step6,這個過程會經歷很多次,直到完成所有配置類的解析和bean的註冊

從上面過程中可以瞭解到

  1. 可以在配置類上面加上@Conditional註解,來控制是否需要解析這個配置類,配置類如果不被解析,那麼這個配置上面6種註解的解析都會被跳過
  2. 可以在被註冊的bean上面加上@Conditional註解,來控制這個bean是否需要註冊到spring容器中
  3. 如果配置類不會被註冊到容器,那麼這個配置類解析所產生的所有新的配置類及所產生的所有新的bean都不會被註冊到容器

一個配置類被spring處理有2個階段:配置類解析階段、bean註冊階段(將配置類作爲bean被註冊到spring容器)。

如果將Condition接口的實現類作爲配置類上@Conditional中,那麼這個條件會對兩個階段都有效,此時通過Condition是無法精細的控制某個階段的,如果想控制某個階段,比如可以讓他解析,但是不能讓他註冊,此時就就需要用到另外一個接口了:ConfigurationCondition

ConfigurationCondition接口相對於Condition接口多了一個getConfigurationPhase方法,用來指定條件判斷的階段,是在解析配置類的時候過濾還是在創建bean的時候過濾

@Conditional使用的3步驟

  1. 自定義一個類,實現Condition或ConfigurationCondition接口,實現matches方法
  2. 在目標對象上使用@Conditional註解,並指定value的指爲自定義的Condition類型
  3. 啓動spring容器加載資源,此時@Conditional就會起作用了

自定義的註解上面也可以使用@Conditional註解

指定Condition的順序
自定義的Condition可以實現PriorityOrdered接口或者繼承Ordered接口,或者使用@Order註解,通過這些來指定這些Condition的優先級。
排序規則:先按PriorityOrdered排序,然後按照order的值進行排序;也就是:PriorityOrdered asc,order值 asc

ConfigurationCondition類
判斷bean存不存在的問題,通常會使用ConfigurationCondition這個接口,階段爲:REGISTER_BEAN,這樣可以確保條件判斷是在bean註冊階段執行的。

對springboot比較熟悉的,它裏面有很多@Conditionxxx這樣的註解,可以去看一下這些註解,很多都實現了ConfigurationCondition接口。

@Profile 的定義,判斷條件在 ProfileCondition 類中

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {
	String[] value();

}

@Conditional註解是被下面這個類處理的
org.springframework.context.annotation.ConfigurationClassPostProcessor

總結

  1. @Conditional註解可以標註在spring需要處理的對象上(配置類、@Bean方法),相當於加了個條件判斷,通過判斷的結果,讓spring覺得是否要繼續處理被這個註解標註的對象
  2. spring處理配置類大致有2個過程:解析配置類、註冊bean,這兩個過程中都可以使用@Conditional來進行控制spring是否需要處理這個過程
  3. Condition默認會對2個過程都有效
  4. ConfigurationCondition控制得更細一些,可以控制到具體那個階段使用條件判斷

參考

通過條件來控制bean的註冊

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章