Spring 條件Bean配置實現
本文介紹Spring 4引入的一個新特性,條件實例化Bean。我們首先了解下Spring4之前的實現方式,接着學習Spring4 提供的Condition接口和 @Conditional註解。
1. 概述
有時需要根據環境創建依賴注入bean,環境可能是正在運行的操作系統、或應用服務器等。Bean配置也可能依賴具體哪個Java版本,環境變量的屬性值,或你的應用正運行的階段,如開發、測試、生產階段等。諸如此類情況,我們需要根據條件實例化bean。
2. Spring 3 實現
2.1. Spring Expression Language (SpEL)實現
Spring3 提供了Spring Expression Language (SpEL),利用SpEl可以實現根據條件裝載Bean。下面通過示例進行說明:
public class MyDao {
@Value("#{systemProperties['os.arch'].equals('x86') ? winDataSource : unixDataSource}")
private DataSource datasource;
...
}
這裏在@Value註解中通過SpEl表達式進行條件加載。
2.2. Profile實現
Spring 3.1 提供了基於Profile的bean配置/創建。即能夠使用配置類基於一定命名環境創建bean。
爲了演示該特性,假設我們應用根據不同環境需要不同的bean配置,如郵件服務Bean依賴不同的操作系統,示例代碼如下:
@Configuration
public class MyConfiguration {
@Bean
public EmailService emailerService(){
if (System.getProperty("os.name").contains("Windows")){
return new WindowsEmailService();
}
return new LinuxEmailService();
}
}
@Bean註解的方法動態返回正確的郵件服務bean。
從Spring 3.1開始,這種硬編碼方法的另一種替代方法(如果需要支持其他操作系統會發生什麼?)是使用Profile實現。通過@Profile定義一組bean。我們例子需要兩個profile,分別對應Windows和Linux。
@Configuration
@Profile("Linux")
public class LinuxConfiguration {
@Bean
public EmailService emailerService() {
return new LinuxEmailService();
}
}
@Profile("Windows")
public class WindowsConfiguration {
@Bean
public EmailService emailerService() {
return new WindowsEmailService();
}
}
profile準備就緒後,可以通過setActiveProfiles()選擇合適的bean:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.getEnvironment().setActiveProfiles("Linux");
context.scan("com.intertech");
context.refresh();
3. Spring 4 實現方式
Spring 4 增加了新的 @Conditional 註解可以實現類似條件配置,但不在需要profile。還是用上面的示例進行說明。需要兩個類分別實現Condition接口,並實現matches()方法,在方法類檢查條件返回boolean值表明是否條件符合。
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class LinuxCondition implements Condition{
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getEnvironment().getProperty("os.name").contains("Linux"); }
}
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class WindowsCondition implements Condition{
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getEnvironment().getProperty("os.name").contains("Windows");
}
}
ConditionContext參數給matches方法提供了訪問環境、容器、類加載器等能力,可以利用這些決定返回是否符合條件。AnnotatedTypeMetadata參數給matches提供訪問增加條件註解的方法。
條件設置好了,現在定義配置類,給創建bean的方法增加條件註解@Conditional,並指定對於的條件類。
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyConfiguration {
@Bean(name="emailerService")
@Conditional(WindowsCondition.class)
public EmailService windowsEmailerService(){
return new WindowsEmailService();
}
@Bean(name="emailerService")
@Conditional(LinuxCondition.class)
public EmailService linuxEmailerService(){
return new LinuxEmailService();
}
}
雖然兩個bean都命名爲“emailerService”,但只有其中一個被容器調用基於條件創建bean。
該示例僅使用創建bean的條件。實際上這些條件可以作爲可重用的機制,用於對各種bean進行條件設置。
4. 總結
本文帶你瞭解Spring不同版本實現條件化實現Bean機制。條件Bean是Spring Boot簡化配置的底層實現機制,希望本文能夠加深你對Spring的理解和青睞。