参考文章: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