更多Spring文章,歡迎點擊 一灰灰Blog-Spring專題java
前面幾篇關於Bean的基礎博文中,主要集中在Bean的定義和使用,但實際的狀況中有沒有一些場景是不加載我定義的bean,或者只有知足某些前提條件的時候才加載我定義的Bean呢?git
本篇博文將主要介紹bean的加載中,條件註解@Conditional
的相關使用github
<!-- more -->web
@Conditional
註解這個註解在Spring4中引入,其主要做用就是判斷條件是否知足,從而決定是否初始化並向容器註冊Beanspring
@Conditional
註解定義以下,其內部主要就是利用了Condition接口,來判斷是否知足條件,從而決定是否須要加載Beanapp
@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
,它持有很多有用的對象,能夠用來獲取不少系統相關的信息,來豐富條件判斷,接口定義以下dom
public interface ConditionContext { // 獲取Bean定義 BeanDefinitionRegistry getRegistry(); // 獲取Bean工程,所以就能夠獲取容器中的全部bean @Nullable ConfigurableListableBeanFactory getBeanFactory(); // environment 持有全部的配置信息 Environment getEnvironment(); // 資源信息 ResourceLoader getResourceLoader(); // 類加載信息 @Nullable ClassLoader getClassLoader(); }
經過一個小例子,簡單的說一下如何使用Condition和@Conditional
註解,來實現bean的條件加載ide
首先咱們定義一個隨機產生數據的類,其功能就是隨機生成一些數據spring-boot
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就不會建立
針對上面的配置與實現,寫一個測試類以下
@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以後,再次測試以下圖
上面的測試演示了經過配置文件選擇注入Bean的狀況,若是一個Bean是經過自動掃描加載的,是否能夠直接在Bean的類上添加註解來決定是否載入呢?
從使用來說,和前面的沒有什麼區別,只是將註解放在具體的類上而言,一樣給出一個示例,先定義一個bean
@Component @Conditional(ScanDemoCondition.class) public class ScanDemoBean { @Value("${conditional.demo.load}") private boolean load; public boolean getLoad() { return load; } }
對應的判斷條件以下,當配置文件中conditional.demo.load
爲true時,纔會加載這個配置,不然不實例化
public class ScanDemoCondition implements Condition { @Override public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { return "true".equalsIgnoreCase(conditionContext.getEnvironment().getProperty("conditional.demo.load")); } }
測試類和前面差很少,稍微注意下的就是自動注入時,改一下必要條件,避免bean不存在時報錯
@Autowired(required = false) private ScanDemoBean scanDemoBean; @GetMapping(path = "/scan") public String showDemo() { String type = environment.getProperty("conditional.demo.load"); if (scanDemoBean == null) { return "not exists! >>>" + type; } else { return "load : " + scanDemoBean.getLoad() + " >>>" + type; } }
當配置爲true時,bean應該存在,走上面的else邏輯
當配置爲false時,不會加載bean,走if邏輯
經過@Conditional
註解配合Condition
接口,來決定給一個bean是否建立和註冊到Spring容器中,從而實現有選擇的加載bean
這樣作的目的是什麼呢?
上面能夠控制bean的建立,但經過上面的流程,會發現有一點繁瑣,有沒有什麼方式能夠簡化上面的流程呢?
只用一個註解就好,不要本身再來實現Condtion接口,Spring框架提供了一系列相關的註解,以下表
註解 | 說明 |
---|---|
@ConditionalOnSingleCandidate |
當給定類型的bean存在而且指定爲Primary的給定類型存在時,返回true |
@ConditionalOnMissingBean |
當給定的類型、類名、註解、暱稱在beanFactory中不存在時返回true.各種型間是or的關係 |
@ConditionalOnBean |
與上面相反,要求bean存在 |
@ConditionalOnMissingClass |
當給定的類名在類路徑上不存在時返回true,各種型間是and的關係 |
@ConditionalOnClass |
與上面相反,要求類存在 |
@ConditionalOnCloudPlatform |
當所配置的CloudPlatform爲激活時返回true |
@ConditionalOnExpression |
spel表達式執行爲true |
@ConditionalOnJava |
運行時的java版本號是否包含給定的版本號.若是包含,返回匹配,不然,返回不匹配 |
@ConditionalOnProperty |
要求配置屬性匹配條件 |
@ConditionalOnJndi |
給定的jndi的Location 必須存在一個.不然,返回不匹配 |
@ConditionalOnNotWebApplication |
web環境不存在時 |
@ConditionalOnWebApplication |
web環境存在時 |
@ConditionalOnResource |
要求制定的資源存在 |
基礎篇
應用篇
一灰灰的我的博客,記錄全部學習和工做中的博文,歡迎你們前去逛逛
盡信書則不如,以上內容,純屬一家之言,因我的能力有限,不免有疏漏和錯誤之處,如發現bug或者有更好的建議,歡迎批評指正,不吝感激
一灰灰blog
知識星球