SpringBoot SpEL關於@ConditionalOnExpression註解,在使用spel表達式引用配置屬性bean導致提前初始化,無綁定數據的問題及相應的解決方法。

springboot 關於@ConditionalOnExpression註解,在使用spel表達式引用配置屬性bean導致提前初始化,無綁定數據的問題及相應的解決方法。

SpringBoot版本

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.7</version>
    </parent>

原始需求

一開始我的需求就是想通過yml配置文件裏的數組結構的值,從而判斷指定bean是否進行註冊到IOC容器中。
不過後來通過觀察Environment bean的注入結果,發現裏面的屬性都被扁平化了。
例如:

students:
  groups:
    - xiaoming
    - xiaowang

在Environment裏的體現爲:
students.groups[0]=xiaoming
students.groups[1]=xiaowang

後來想通過@ConfigurationProperties配置bean,將yml文件的數組結構映射到下邊的配置bean。

public class Student{
   private List<Student> groups;
// ...get  ...set
}

最後,值是映射到groups裏了,到這一步是ok的。

然後通過@ConditionalOnExpression註解使用spel,結果導致 配置bean提前初始化,狀態不完整,所以groups屬性等於null。
通過看官方springboot文檔,對其解釋引用的bean會被提前初始化,導致狀態不完整

最後也是隻能作罷。

最後只能自己實現了。沒招了。

問題原因

org.springframework.boot.autoconfigure.condition.ConditionalOnExpression註解的註釋說明了,在spel引用的bean會提前初始化,導致bean的狀態不完整導致的。

@ConditionalOnExpression註解雖然使用可以使用spel引用bean,但是我覺的還是適用到那些標記Bean或通過配置值進行判斷的情況。
如果在#{ @xxxBean.property }的話,那這時候xxxBean下的屬性可能會爲null,也就是配置文件的屬性值沒綁定到bean中。

關於@ConditionalOnExpression註解的討論

Github 註釋方法時屬性值未正確加載@ConditionalOnExpression

解決方法

第一種,直接使用@ConditionalOnExpression註解的spel表達式不引用配置bean了,通過獲取配置文件的屬性直接進行條件的限定。

// 以下表達式就是當 值 在數組裏包含,則返回true,否則返回false。經測試,可行。
@ConditionalOnExpression(value = "#{ T(org.springframework.boot.context.properties.bind.Binder).get(environment).bind('yml配置裏的數組結構的完整Key', T(org.springframework.boot.context.properties.bind.Bindable).listOf(T(String))).get().contains('你想包含的數組值') }")

第二種就是自己將以上的邏輯包裝爲註解。

實現邏輯類需要繼承org.springframework.boot.autoconfigure.condition.SpringBootCondition類,方便實現,裏面加入了共用的邏輯,你只需要依葫蘆畫瓢就行了。
然後在你的自定義註解上加上org.springframework.context.annotation.Conditional註解,value=你自己實現的條件類,這樣就可以了。

最後使用,只需要放在你需要進行條件限定的類,並加上屬性和值的指定,基本就ok了。

參閱資源

SpEL expressions-types
SpEL Expression Conditions
stackoverflow conditionalonexpression

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