SpringBoot重點詳解--@Conditional註解

目錄

@Conditional註解

自定義Conditional

SpringBoot 擴展註解


在上一章 SpringBoot重點詳解--配置文件》中曾簡單介紹過如何利用 @Profile 註解來根據指定 profile 是否被激活動態地決定是否要創建某一個 Bean 。

在這一章,我們將介紹另一種根據條件來裝配 Bean 的新方法:使用 @Conditional 註解,根據是否滿足指定的條件來決定是否裝配 Bean 。

@Conditional註解

Conditional 是由 SpringFramework 提供的一個註解,位於 org.springframework.context.annotation 包內,定義如下。

  1. package org.springframework.context.annotation;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. import java.lang.annotation.Target;
  6. @Retention(RetentionPolicy.RUNTIME)
  7. @Target({ElementType.TYPE, ElementType.METHOD})
  8. public @interface Conditional {
  9. Class<? extends Condition>[] value();
  10. }

Conditional 註解類裏只有一個 value 屬性,需傳入一個 Condition 類型的數組,我們先來看看這個 Condition 接口長什麼樣。

  1. package org.springframework.context.annotation;
  2. import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
  3. import org.springframework.core.type.AnnotatedTypeMetadata;
  4. public interface Condition {
  5. boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
  6. }

其中,matches() 方法傳入的參數 ConditionContext 是專門爲 Condition 而設計的一個接口類,可以從中獲取到Spring容器的以下對象信息。

當一個 Bean 被 Conditional 註解修飾時,Spring容器會對數組中所有 Condition 接口的 matches() 方法進行判斷,只有當其中所有 Condition 接口的 matches()方法都爲 ture 時,纔會創建 Bean 。

自定義Conditional

接下來,我們將以一個國際化 I18n Bean 動態創建爲例(根據配置中的 i18n.lang 屬性值來動態地創建國際化 I18n Bean),對如何使用 Conditional 註解進行簡單舉例:

  • 當 i18n.lang=zh_CN 就創建中文 I18nChs Bean, 
  • 當 i18n.lang=en_US 就創建英文 I18nEng Bean。  

創建好的兩個 Condition 實現類 I18nChsCondition 和 I18nEngCondition 代碼如下。

  1. public class I18nChsCondition extends SpringBootCondition {
  2. @Override
  3. public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
  4. String lang = context.getEnvironment().getProperty("i18n.lang");
  5. ConditionOutcome outCome = new ConditionOutcome("zh_CN".equals(lang), "i18n.lang=" + lang);
  6. return outCome;
  7. }
  8. }
  1. public class I18nEngCondition extends SpringBootCondition {
  2. @Override
  3. public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
  4. String lang = context.getEnvironment().getProperty("i18n.lang");
  5. ConditionOutcome outCome = new ConditionOutcome("en_US".equals(lang), "i18n.lang=" + lang);
  6. return outCome;
  7. }
  8. }

I18n 接口定義如下。

  1. public interface I18n {
  2. // 獲取 name 屬性的值
  3. String i18n(String name);
  4. }

I18n 接口的兩個實現類 I18nChs 和 I18nEng 定義如下。

  1. @Component
  2. @Conditional(I18nChsCondition.class)
  3. public class I18nChsImpl implements I18n {
  4. Map<String, String> map = new HashMap<String, String>() {
  5. private static final long serialVersionUID = 1L;
  6. {
  7. put("lang", "中文");
  8. }
  9. };
  10. @Override
  11. public String i18n(String name) {
  12. return map.get(name);
  13. }
  14. }
  1. @Component
  2. @Conditional(I18nEngCondition.class)
  3. public class I18nEngImpl implements I18n {
  4. Map<String, String> map = new HashMap<String, String>() {
  5. private static final long serialVersionUID = 1L;
  6. {
  7. put("lang", "English");
  8. }
  9. };
  10. @Override
  11. public String i18n(String name) {
  12. return map.get(name);
  13. }
  14. }

在啓動類中添加測試代碼代碼如下。

  1. @SpringBootApplication
  2. public class App
  3. {
  4. public static void main( String[] args )
  5. {
  6. ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
  7. I18n i18n = context.getBean(I18n.class);
  8. System.out.println(i18n.getClass().getName());
  9. System.out.println(i18n.i18n("lang"));
  10. context.close();
  11. }
  12. }

配置 application.properties 內容如下:

  1. # language : zh_CN/Chinese,en_US/America
  2. i18n.lang=zh_CN

運行程序,打印結果:

  1. com.pengjunlee.condition.I18nChsImpl
  2. 中文

配置 application.properties 內容如下:

  1. # language : zh_CN/Chinese,en_US/America
  2. i18n.lang=en_US

再次運行程序,打印結果:

  1. com.pengjunlee.condition.I18nEngImpl
  2. English

爲了書寫和調用方便,我們還可以把上面的條件定義成註解,以 I18nChsCondition 爲例,定義代碼如下。

  1. @Target({ ElementType.TYPE, ElementType.METHOD })
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Conditional(I18nChsCondition.class)
  5. public @interface I18nChs {
  6. }

將 I18nChs 註解添加到 I18nChsImpl 上。

  1. @Component
  2. @I18nEng
  3. public class I18nChsImpl implements I18n {//內容同上,此處省略}

SpringBoot 擴展註解

從上面的示例不難看出,如果要使用我們自定義條件類實現起來還是有點小麻煩的,不過比較慶幸的是, SpringBoot 在  Conditional 註解的基礎上已經提前爲我們定義好了一系列功能豐富的註解,我們可以直接使用。

接下來我們使用  ConditionalOnProperty 註解來實現上面的國際化示例。

僅需修改 I18nChsImpl 和 I18nEngImpl 兩個實現組件類,其他代碼不變,程序執行結果與之前相同。

  1. @Component
  2. @ConditionalOnProperty(name = "i18n.lang", havingValue = "zh_CN", matchIfMissing = true)
  3. public class I18nChsImpl implements I18n {//內容同上,此處省略}
  1. @Component
  2. @ConditionalOnProperty(name = "i18n.lang", havingValue = "en_US", matchIfMissing = false)
  3. public class I18nEngImpl implements I18n {//內容同上,此處省略}

本項目源碼已上傳至CSDN,資源地址:https://download.csdn.net/download/pengjunlee/10310020
 

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