【10分鍾學Spring】:@Profile、@Conditional實現條件化裝配


根據不同的環境來裝配不同的bean

企業級開發中,我們一般有多種環境,比如開發環境、測試環境、UAT環境和生產環境。而系統中有些配置是和環境強相關的,比如數據庫相關的配置,與其他外部系統的集成等。

如何才能實現一個部署包適用於多種環境呢?

Spring給我們提供了一種解決方案,這便是條件化裝配bean的機制。最重要的是這種機制是在運行時決定該注入適用於哪個環境的bean對象,不需要重新編譯構建。

下面使用Spring的profile機制實現dataSource對象的條件化裝配。

1、給出開發環境、測試環境、生產環境dataSource的不同實現類

說明:此處只為演示條件化裝配bean,不做真實數據源對象模擬。

public interface DataSource {
    void show();
}

public class DevDataSource implements DataSource{
    
    public DevDataSource(){
        show();
    }
    public void show() {
        System.out.println("開發環境數據源對象");
    }
}

public class TestDataSource implements DataSource{

    public TestDataSource() {
        show();
    }

    public void show() {
        System.out.println("測試環境數據源對象");
    }
}

public class ProDataSource implements DataSource{

    public ProDataSource() {
        show();
    }

    public void show() {
        System.out.println("生產環境數據源對象");
    }
}

2、使用profile配置條件化bean

其實profile的原理就是將不同的bean定義綁定到一個或多個profile之中,在將應用部署到不同的環境時,確保對應的profile處於激活狀態即可。

這里我們使用JavaConfig的方式配置profile bean

@Configuration
public class DataSourceConfig {
    
    @Bean
    @Profile("dev")
    public DataSource devDataSource(){
        return new DevDataSource();
    }

    @Bean
    @Profile("test")
    public DataSource testDataSource(){
        return new TestDataSource();
    }

    @Bean
    @Profile("pro")
    public DataSource proDataSource(){
        return new ProDataSource();
    }
}

可以看到我們使用了@Profile注解,將不同環境的bean綁定到了不同的profile中。

3、激活profile

只要上面的兩步還不行,我們還必須激活profile,這樣Spring會依據激活的哪個profile,來創建並裝配對應的bean對象。

激活profile需要兩個屬性。

spring.profiles.active
spring.profiles.default

可以在web.xml中配置Web應用的上下文參數,來激活profile屬性。比如在web.xml中增加如下配置來激活dev的profile:

    <context-param>
        <param-name>spring.profiles.active</param-name>
        <param-value>dev</param-value>
    </context-param>

4、測試條件化裝配

使用@ActiveProfiles注解在測試類中激活指定profile。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {DataSourceConfig.class})
@ActiveProfiles("dev")
public class TestConditionDataSource {

    @Autowired
    private DataSource dataSource;

    @Test
    public void testDataSource(){
        Assert.assertNotNull(dataSource);
    }

}

輸出:

開發環境數據源對象

我們profile換成生產環境的pro試下,

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {DataSourceConfig.class})
@ActiveProfiles("pro")
public class TestConditionDataSource {

    @Autowired
    private DataSource dataSource;

    @Test
    public void testDataSource(){
        Assert.assertNotNull(dataSource);
    }

}

輸出:

生產環境數據源對象

通過spring的profile機制,我們實現了不同環境dataSource數據源對象的條件化裝配。比較簡單,就兩步:1、使用@Profile注解為不同的bean配置profile(當然這里也可以是xml的方式),2、根據不同環境激活不同的profile。

使用@Conditional注解實現條件化的bean

Spring 4.0引入的新注解@Conditional注解,它可以用到帶有@Bean注解的方法上,如果給定的條件計算結果為true,就會創建這個bean,否則不創建。

1、我們創建一個helloWorld對象

public class HelloWorld {

    public void sayHello(){
        System.out.println("conditional 裝配helloworld");
    }
}

2、創建配置類

在該配置類中我們首先使用了@PropertySource注解加載了屬性文件hello.properties,其次可以看到在helloWorld的bean配置中,除了@Bean注解外,多了一個@Conditional注解,不錯,@Conditional注解是我們實現條件化裝配bean的核心注解。

@Conditional注解中有一個HelloWorldConditional類,該類定義了我們創建該bean對象的條件。

@Configuration
@PropertySource("classpath:hello.properties")
public class HelloWorldConfig {

    @Bean
    @Conditional(HelloWorldConditional.class)
    public HelloWorld helloWorld(){
        return new HelloWorld();
    }
}

3、創建條件類HelloWorldConditional,需要實現Condition接口。

實現了Condition接口,重寫了matches方法,在該方法中我們檢測了環境變量中是否有hello屬性,如果有就創建。沒有則忽略。

注意:hello.properties中屬性會存儲到spring的Environment對象中,因此我們可以檢測到其中的屬性是否存在。

public class HelloWorldConditional implements Condition {
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        return conditionContext.getEnvironment().containsProperty("hello");
    }
}

4、測試條件裝配

public class HelloWorldConditionTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(HelloWorldConfig.class);
        HelloWorld helloWorld = applicationContext.getBean("helloWorld",HelloWorld.class);
        helloWorld.sayHello();
    }
}

開始,我們在hello.properties中增加一條屬性,

在這里插入圖片描述

運行測試示例,會輸出:

conditional 裝配helloworld

說明此時,bean已成功裝配。

如果我們注釋掉hello.properties的這行屬性。再次運行示例,則會提示bean不存在。

在這里插入圖片描述
提示沒有“helloWorld”的bean對象,說明了條件不滿足不會創建bean對象。

在這里插入圖片描述

總結

Spring條件化裝配bean的兩種方式,第一種是使用profile機制,在bean的配置類中使用@profile注解,標識哪些bean對應哪個profile配置,然后在web.xml或Servlet啟動參數中配置激活哪個profile來實現條件裝配;第二種是使用@Conditional注解,在帶有@Bean注解的方法上增加@Conditional注解,在注解屬性值中提供一個實現了Condition接口的類(該類會重寫matches方法,定義具體的創建條件)。<完>

1574428873195184.png


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM