SpringBoot基礎篇Bean之條件注入@Condition使用姿勢
前面幾篇關於Bean的基礎博文中,主要集中在Bean的定義和使用,但實際的情況中有沒有一些場景是不載入我定義的bean,或者只有滿足某些前提條件的時候才載入我定義的Bean呢?
本篇博文將主要介紹bean的載入中,條件註解@Conditional
的相關使用
I. @Conditional
註解
這個註解在Spring4中引入,其主要作用就是判斷條件是否滿足,從而決定是否初始化並向容器註冊Bean
1. 定義
@Conditional
註解定義如下,其內部主要就是利用了Condition介面,來判斷是否滿足條件,從而決定是否需要載入Bean
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Conditional { Class<? extends Condition>[] value(); }
下面是Condtion
介面的定義,這個可以說是最基礎的入口了,其他的所有條件註解,歸根結底,都是通過實現這個介面進行擴充套件的
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
}
這個介面中,有個引數比較有意思ConditionContext
,它持有不少有用的物件,可以用來獲取很多系統相關的資訊,來豐富條件判斷,介面定義如下
public interface ConditionContext { // 獲取Bean定義 BeanDefinitionRegistry getRegistry(); // 獲取Bean工程,因此就可以獲取容器中的所有bean @Nullable ConfigurableListableBeanFactory getBeanFactory(); // environment 持有所有的配置資訊 Environment getEnvironment(); // 資源資訊 ResourceLoader getResourceLoader(); // 類載入資訊 @Nullable ClassLoader getClassLoader(); }
2. 使用說明
通過一個小例子,簡單的說一下如何使用Condition和@Conditional
註解,來實現bean的條件載入
首先我們定義一個隨機產生資料的類,其功能就是隨機生成一些資料
public class RandDataComponent<T> { private Supplier<T> rand; public RandDataComponent(Supplier<T> rand) { this.rand = rand; } public T rand() { return rand.get(); } }
我們目前提供兩種隨機資料生成的bean,但是需要根據配置來選擇具體選中的方式,因此我們如下定義Bean
@Configuration
public class ConditionalAutoConfig {
@Bean
@Conditional(RandIntCondition.class)
public RandDataComponent<Integer> randIntComponent() {
return new RandDataComponent<>(() -> {
Random random = new Random();
return random.nextInt(1024);
});
}
@Bean
@Conditional(RandBooleanCondition.class)
public RandDataComponent<Boolean> randBooleanComponent() {
return new RandDataComponent<>(() -> {
Random random = new Random();
return random.nextBoolean();
});
}
}
上面的配置,先不管@Conditional
註解的內容,單看兩個Bean的定義,一個是定義int隨機數生成;一個是定義boolean隨機生成;
但是我們的系統中,只需要一個隨機資料生成器即可,我們選擇根據配置conditional.rand.type
的值來選擇到底用哪個,配置如下
# int 表示選擇隨機產生int資料; 非int 表示隨機產生boolean資料
conditional.rand.type=int
接下來就得看這個條件如何加上了,也就是上面配置類ConditionalAutoConfig
中兩個註解的內容了,兩個類都是實現Condition
的介面,具體如下
public class RandBooleanCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
String type = conditionContext.getEnvironment().getProperty("conditional.rand.type");
return "boolean".equalsIgnoreCase(type);
}
}
public class RandIntCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
String type = conditionContext.getEnvironment().getProperty("conditional.rand.type");
return "int".equalsIgnoreCase(type);
}
}
上面的實現也比較清晰,獲取配置值,然後判斷,並返回true/fase;返回true,則表示這個條件滿足,那麼這個Bean就可以被載入了;否則這個Bean就不會建立
3. 測試與驗證
針對上面的配置與實現,寫一個測試類如下
@RestController
@RequestMapping(path = "/conditional")
public class ConditionalRest {
@Autowired
private RandDataComponent randDataComponent;
@GetMapping(path = "/show")
public String show() {
String type = environment.getProperty("conditional.rand.type");
return randDataComponent.rand() + " >>> " + type;
}
}
當配置檔案的值為int時,每次訪問返回的應該都是正整數,演示如下圖
將配置的值改成boolean之後,再次測試如下圖