@ConditionalOnProperty

@ConditionalOnProperty源碼解析

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnPropertyCondition.class)
public @interface ConditionalOnProperty {
    // 數(shù)組,獲取對應property名稱的值,與name不可同時使用
    String[] value() default {};

    // 配置屬性名稱的前綴,比如spring.http.encoding
    String prefix() default "";

    // 數(shù)組,配置屬性完整名稱或部分名稱
    // 可與prefix組合使用,組成完整的配置屬性名稱,與value不可同時使用
    String[] name() default {};

    // 可與name組合使用,比較獲取到的屬性值與havingValue給定的值是否相同,相同才加載配置
    String havingValue() default "";

    // 缺少該配置屬性時是否可以加載。如果為true,沒有該配置屬性時也會正常加載;反之則不會生效
    boolean matchIfMissing() default false;
}

最近在寫一個“啟動時導入數(shù)據(jù)”的小功能。實現(xiàn)很簡單, CommandLineRunner會在SpringBoot啟動時運行,第一版長這樣:

@Order(1)
@Component
public class DictionaryInitializer implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
          //do import dictionary
    }
}

自然的,我們希望這個可配置化,只在需要的時候運行。我們使用

@ConditionalOnProperty
@Order(1)
@Component
@ConditionalOnProperty(name="app.initialize.dictionary", havingValue="true")
public class DictionaryInitializer implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
          //do import dictionary
    }
}

然后在application.properties中加入

app.initialize.dictionary=true

隨著功能的迭代,我們又更多類似的導入功能,例如導入Product和Parameter。依樣畫葫蘆,application.properties中多了相似的配置

app.initialize.dictionary=true
app.initialize.product=true
app.initialize.parameter=true

進一步優(yōu)化,隨著導入功能的增多。我們可能需要一個一鍵導入的功能,同時也要保留原來的功能。

app.initialize.all=true

這個有點麻煩,涉及到多個條件的組合。ConditionalOnProperty是支持 “多個條件邏輯與”的

@ConditionalOnProperty(name={"app.initialize.dictionary","app.initialize.all"}, havingValue="true")

當然這個不符合我們的要求,我們需要“邏輯或”
首先一個類是不能標注多個相同annotation的,編譯通不過。

@Order(1)
@Component
??@ConditionalOnProperty(name="app.initialize.dictionary", havingValue="true")
??@ConditionalOnProperty(name="app.initialize.all", havingValue="true")
public class DictionaryInitializer implements CommandLineRunner

@ConditionalOnProperty 本身也并沒有這樣的功能
一種繁瑣的做法,是自定義條件,繼承AnyNestedCondition

class DicOrAllCondition extends AnyNestedCondition {

    public DicOrAllCondition() {
        super(ConfigurationPhase.PARSE_CONFIGURATION);
    }

    @ConditionalOnProperty(name = "app.initialize.dictionary", value = "true")
    static class DicCondition {
    }

    @ConditionalOnProperty(name = "app.initialize.all", value = "true")
    static class AllCondition {
    }
}


@Order(1)
@Component
@Conditional(DicOrAllCondition.class)
public class DictionaryInitializer implements CommandLineRunner{}

還有一種比較靈活的方式是使用@ConditionalOnExpression寫一個表達式

@Order(1)
@Component
@ConditionalOnExpression("${app.initialize.dictionary:false} || ${app.initialize.all:false}")
public class DictionaryInitializer implements CommandLineRunner{}

精益無止境,其實還有更靈活的配置方式:除了dictionary其他都導入:

app.initialize.all=true
app.initialize.dictionary=false

換種說法就是:如果局部有配置,則按局部配置處理。如果局部沒配置,則按全局配置處理。如果全局、局部都沒配置,則默認不導入。這里可以利用“默認值”嵌套表達式實現(xiàn)。

@ConditionalOnExpression("${app.initialize.dictionary:${app.initialize.all:false}}")
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容