1. 概念
預設情況下,Spring中定義的bean在應用程式啟動時會全部裝配,不管目前運作的是哪個環境(Dev,QA或者Prod),也不管目前運作的是什麼系統(Windows或者Linux),但有些使用場景下,我們可能需要條件化的裝配某些bean,即當滿足某一條件時,裝配某些bean,當不滿足某一條件時,就忽略掉某些bean。
這個條件可以很簡單,比如當某個jar包存在時,當存在某個環境變量時,也可以很複雜。
針對這個使用場景,Spring中提供了
@Conditional
注解來實作條件化的bean。
2. 示例
為了更好的了解,我們通過具體的代碼示例來了解下條件化的bean的實作方式。
由于Windows系統和Linux系統顯示清單的指令不同,Windows下是dir,Linux下是ls,是以我們的需求是,應用程式啟動時自動根據目前系統裝配需要的bean,比如我的電腦系統是Windows 7,那就隻裝配Windows系統所需要的bean。
首先,定義一個接口ListService,該接口隻包含一個方法showListCmd:
package chapter03.conditional;
public interface ListService {
String showListCmd();
}
然後定義該接口的2個實作類WindowsListService和LinuxListService:
package chapter03.conditional;
public class WindowsListService implements ListService {
public WindowsListService() {
System.out.println("This is WindowsListService constructor");
}
@Override
public String showListCmd() {
return "dir";
}
}
package chapter03.conditional;
public class LinuxListService implements ListService {
public LinuxListService() {
System.out.println("This is LinuxListService constructor");
}
@Override
public String showListCmd() {
return "ls";
}
}
然後分别定義Windows系統和Linux系統的判斷條件:
package chapter03.conditional;
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 conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
return conditionContext.getEnvironment().getProperty("os.name").contains("Windows");
}
}
package chapter03.conditional;
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 conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
return conditionContext.getEnvironment().getProperty("os.name").contains("Linux");
}
}
值得注意的是,這2個類都需要實作Condition接口,并重寫方法matches(),如果該方法傳回true時,使用該條件的1個或多個bean就會被裝配,如果該方法傳回false,使用該條件的1個或多個bean就會被忽略。
然後,定義Java配置類ConditionalConfig:
package chapter03.conditional;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ConditionalConfig {
@Bean
@Conditional(WindowsCondition.class)
public ListService windowsListService() {
return new WindowsListService();
}
@Bean
@Conditional(LinuxCondition.class)
public ListService linuxListService() {
return new LinuxListService();
}
}
這裡聲明bean時除了使用@Bean注解,還使用了@Conditional注解,這個注解是實作條件化的bean的關鍵,它的參數可以傳遞任何實作了Condition接口并重寫了matches()方法的類,這裡傳遞的是我們上面定義的WindowsCondition和LinuxCondition。
最後,我們定義一個Main類,在其main()方法中添加如下測試代碼:
package chapter03.conditional;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConditionalConfig.class);
ListService listService = context.getBean(ListService.class);
System.out.println(context.getEnvironment().getProperty("os.name") + "系統下的清單指令為:" + listService.showListCmd());
context.close();
}
}
運作結果如下所示:
This is WindowsListService constructor
Windows 7系統下的清單指令為:dir
從運作日志可以看出,由于目前系統是Windows 7,我們聲明的linuxListService bean并沒有被裝配。
3. 源碼及參考
源碼位址:https://github.com/zwwhnly/spring-action.git,歡迎下載下傳。
汪雲飛《Java EE開發的颠覆者:Spring Boot實戰》
Craig Walls 《Spring實戰(第4版)》
4. 最後
打個小廣告,歡迎掃碼關注微信公衆号:「申城異鄉人」,定期分享Java技術幹貨,讓我們一起進步。