Springboot的三种条件装配


参考文章: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();
    }
}
BeanTestConfig

然后修改配置文件的环境配置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

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM