【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方法,定義具體的建立條件)。<完>