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