目錄
在上一章 《SpringBoot重點詳解--配置文件》中曾簡單介紹過如何利用 @Profile 註解來根據指定 profile 是否被激活動態地決定是否要創建某一個 Bean 。
在這一章,我們將介紹另一種根據條件來裝配 Bean 的新方法:使用 @Conditional 註解,根據是否滿足指定的條件來決定是否裝配 Bean 。
@Conditional註解
Conditional 是由 SpringFramework 提供的一個註解,位於 org.springframework.context.annotation 包內,定義如下。
- package org.springframework.context.annotation;
-
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
- @Retention(RetentionPolicy.RUNTIME)
- @Target({ElementType.TYPE, ElementType.METHOD})
- public @interface Conditional {
-
- Class<? extends Condition>[] value();
-
- }
Conditional 註解類裏只有一個 value 屬性,需傳入一個 Condition 類型的數組,我們先來看看這個 Condition 接口長什麼樣。
- package org.springframework.context.annotation;
-
- import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
- import org.springframework.core.type.AnnotatedTypeMetadata;
- public interface Condition {
-
- boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
-
- }
其中,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 代碼如下。
- public class I18nChsCondition extends SpringBootCondition {
-
- @Override
- public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
- String lang = context.getEnvironment().getProperty("i18n.lang");
- ConditionOutcome outCome = new ConditionOutcome("zh_CN".equals(lang), "i18n.lang=" + lang);
- return outCome;
- }
- }
- public class I18nEngCondition extends SpringBootCondition {
-
- @Override
- public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
- String lang = context.getEnvironment().getProperty("i18n.lang");
- ConditionOutcome outCome = new ConditionOutcome("en_US".equals(lang), "i18n.lang=" + lang);
- return outCome;
- }
-
- }
I18n 接口定義如下。
- public interface I18n {
-
- // 獲取 name 屬性的值
- String i18n(String name);
-
- }
I18n 接口的兩個實現類 I18nChs 和 I18nEng 定義如下。
- @Component
- @Conditional(I18nChsCondition.class)
- public class I18nChsImpl implements I18n {
-
- Map<String, String> map = new HashMap<String, String>() {
-
- private static final long serialVersionUID = 1L;
-
- {
- put("lang", "中文");
- }
- };
-
- @Override
- public String i18n(String name) {
- return map.get(name);
- }
- }
- @Component
- @Conditional(I18nEngCondition.class)
- public class I18nEngImpl implements I18n {
-
- Map<String, String> map = new HashMap<String, String>() {
-
- private static final long serialVersionUID = 1L;
-
- {
- put("lang", "English");
- }
- };
-
- @Override
- public String i18n(String name) {
- return map.get(name);
- }
-
- }
在啓動類中添加測試代碼代碼如下。
- @SpringBootApplication
- public class App
- {
- public static void main( String[] args )
- {
- ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
- I18n i18n = context.getBean(I18n.class);
- System.out.println(i18n.getClass().getName());
- System.out.println(i18n.i18n("lang"));
- context.close();
- }
- }
配置 application.properties 內容如下:
# language : zh_CN/Chinese,en_US/America i18n.lang=zh_CN
運行程序,打印結果:
com.pengjunlee.condition.I18nChsImpl 中文
配置 application.properties 內容如下:
# language : zh_CN/Chinese,en_US/America i18n.lang=en_US
再次運行程序,打印結果:
com.pengjunlee.condition.I18nEngImpl English
爲了書寫和調用方便,我們還可以把上面的條件定義成註解,以 I18nChsCondition 爲例,定義代碼如下。
- @Target({ ElementType.TYPE, ElementType.METHOD })
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- @Conditional(I18nChsCondition.class)
- public @interface I18nChs {
-
- }
將 I18nChs 註解添加到 I18nChsImpl 上。
- @Component
- @I18nEng
- public class I18nChsImpl implements I18n {//內容同上,此處省略}
SpringBoot 擴展註解
從上面的示例不難看出,如果要使用我們自定義條件類實現起來還是有點小麻煩的,不過比較慶幸的是, SpringBoot 在 Conditional 註解的基礎上已經提前爲我們定義好了一系列功能豐富的註解,我們可以直接使用。
接下來我們使用 ConditionalOnProperty 註解來實現上面的國際化示例。
僅需修改 I18nChsImpl 和 I18nEngImpl 兩個實現組件類,其他代碼不變,程序執行結果與之前相同。
- @Component
- @ConditionalOnProperty(name = "i18n.lang", havingValue = "zh_CN", matchIfMissing = true)
- public class I18nChsImpl implements I18n {//內容同上,此處省略}
- @Component
- @ConditionalOnProperty(name = "i18n.lang", havingValue = "en_US", matchIfMissing = false)
- public class I18nEngImpl implements I18n {//內容同上,此處省略}
本項目源碼已上傳至CSDN,資源地址:https://download.csdn.net/download/pengjunlee/10310020