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