參考文章:https://mp.weixin.qq.com/s/CAVgFuDwazMfU6nXk1gWNw
一、Profile
這種方式是最常見也是最通用的
一般項目都分為開發、測試、線上環境,所以就會有不同的配置文件。
首先聲明兩個不同環境的類,並且Bean創建的配置類
------------------------------------------------------------------------ public class DevBeanTest implements BeanTest{ @Override public void sayHello() { System.out.println("======= devBeanClass ======="); } } ------------------------------------------------------------------------ public class ProdBeanTest implements BeanTest{ @Override public void sayHello() { System.out.println("======= prodBeanClass ======="); } } ------------------------------------------------------------------------

import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; @Configuration public class BeanTestConfig { @Bean @Profile("dev") public BeanTest initBeanTest() { System.out.println("初始化DevBean"); return new DevBeanTest(); } @Bean @Profile("prod") public BeanTest initOSSUploader() { System.out.println("初始化ProdBean"); return new ProdBeanTest(); } }
然后修改配置文件的環境配置application.yml:
spring:
profiles:
active: dev
啟動項目打印如下:
然后配置文件改為prod
也可以如下配置,代表非dev環境
@Bean @Profile("!dev") public BeanTest initOSSUploader() { System.out.println("初始化ProdBean"); return new ProdBeanTest(); }
profile經常被用作配置不同環境的數據庫
二、Conditional
還是使用上面的兩個bean演示:
@Bean // @Profile("dev") @Conditional(DevCondition.class) public BeanTest initBeanTest() { System.out.println("初始化DevBean"); return new DevBeanTest(); } @Bean @Profile("!dev") public BeanTest initOSSUploader() { System.out.println("初始化ProdBean"); return new ProdBeanTest(); }
如上的devBean使用了Conditional進行控制,所以需要再編寫個類如下:
import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; public class DevCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { return "true".equalsIgnoreCase(context.getEnvironment().getProperty("bean.dev")) && "dev".equalsIgnoreCase(context.getEnvironment().getProperty("spring.profiles.active")); } }
如上代碼Matches判斷,是dev環境並且bean.dev為true才進行創建。所以在配置文件中添加 bean.dev=true:
spring: profiles: active: dev bean: dev: true
啟動項目打印如下:
因為Condition要求的是實現,所以也可以不重新新建個實現類,直接使用實例化bean的類實現。這樣看起來也一目了然。如下:
@Bean @Conditional(DevBeanTest.class) public BeanTest initBeanTest() { System.out.println("使用Condition 初始化DevBean"); return new DevBeanTest(); }
import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; public class DevBeanTest implements BeanTest, Condition { @Override public void sayHello() { System.out.println("======= devBeanClass ======="); } /** * 創建當前bean的條件配置 * @param context * @param metadata * @return */ @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { return "true".equalsIgnoreCase(context.getEnvironment().getProperty("bean.dev")) && "dev".equalsIgnoreCase(context.getEnvironment().getProperty("spring.profiles.active")); } }
三、Condition擴展
Spring
提供的條件裝配@Conditional
,靈活性非常強,但是具體判斷邏輯還需要我們自己實現,比較麻煩。
實際上,Spring Boot
為開發者提供了很多使用起來更簡單的條件注解,例如:
- ConditionalOnProperty:如果有指定的配置,條件生效
- ConditionalOnBean:如果有指定的Bean,條件生效
- ConditionalOnMissingBean:如果沒有指定的Bean,條件生效
- ConditionalOnMissingClass:如果沒有指定的Class,條件生效
- ConditionalOnWebApplication:在Web環境中條件生效
- ConditionalOnExpression:根據表達式判斷條件是否生效
ConditionalOnProperty
更改devBean的創建條件如下:
@Bean @ConditionalOnProperty(name={"bean.dev","spring.profiles.active"}) public BeanTest initBeanTest() { System.out.println("使用Condition 初始化DevBean"); return new DevBeanTest(); }
注:這個是錯誤的 運行后可以正常創建,注解的源碼如下:
@Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE, ElementType.METHOD }) @Documented @Conditional(OnPropertyCondition.class) public @interface ConditionalOnProperty { // 數組,獲取對應property名稱的值,與name不可同時使用 String[] value() default {}; // 配置屬性名稱的前綴,比如spring.http.encoding String prefix() default ""; // 數組,配置屬性完整名稱或部分名稱 // 可與prefix組合使用,組成完整的配置屬性名稱,與value不可同時使用 String[] name() default {}; // 可與name組合使用,比較獲取到的屬性值與havingValue給定的值是否相同,相同才加載配置 String havingValue() default ""; // 缺少該配置屬性時是否可以加載。如果為true,沒有該配置屬性時也會正常加載;反之則不會生效 boolean matchIfMissing() default false; }
參考鏈接:https://www.cnblogs.com/secbro/p/12011522.html
查看源碼后發現我們上面的方法是錯誤的,經過測試把配置文件改為prod仍然可以創建。該注解是拿配置文件中的值跟 havingValue的值對比。而havingValue是個String類型,所以只能跟一個值對比,我們的兩個屬性只能為一個值,如果多個不同值,可以使用@ConditionalOnExpression注解根據表達式判斷條件,這個后面演示。
當前我們的配置文件如下:
spring: profiles: active: dev bean: dev: true
@Bean @ConditionalOnProperty(name={"bean.dev","spring.profiles.active"},havingValue = "dev") public BeanTest initBeanTest() { System.out.println("使用Condition 初始化DevBean"); return new DevBeanTest(); }
如果bean改為如上所示,則不能創建了,所以這個注解多條件是"與"的判斷。如果修改配置文件為:
spring:
profiles:
active: dev
bean:
dev: dev
就可以正常創建了。
如果想實現"或"的判斷,或者實現不同屬性和不值的判斷,可以使用這個注解ConditionalOnExpression:
恢復配置文件如下:
spring: profiles: active: dev bean: dev: true
@Bean // @ConditionalOnProperty(name={"bean.dev","spring.profiles.active"},havingValue = "dev") @ConditionalOnExpression("${bean.dev:true} || ${spring.profiles.active:dev}") public BeanTest initBeanTest() { System.out.println("使用Condition 初始化DevBean"); return new DevBeanTest(); }
可以看到創建bean的條件改為
@ConditionalOnExpression("${bean.dev:true} || ${spring.profiles.active:dev}")
運行項目后正常創建。
也可以修改表達式進行值嵌套值判斷如下:
@ConditionalOnExpression("${bean.dev:${spring.profiles.active:dev}}")
因為 spring.profiles.active:dev 這段返回的是Boolean值所以剛好可以跟bean.dev判斷,當然這樣也變成"與"的關系了
參考文章:https://www.jianshu.com/p/9c3802080d9f
https://www.cnblogs.com/secbro/p/12011522.html
https://mp.weixin.qq.com/s/CAVgFuDwazMfU6nXk1gWNw