我們前面已經討論了相當多種取得Bean的方法,如:自動注入(@Autowired、@Resource)或透過BeanFactory進行取得相關Bean元件,但的多開發者都會思考,如果有一種元件能夠一次將所想要的相關Bean類別自動注入成一個集合,並能夠提供給開發者所選擇想要的Bean類別進行過濾,可以更加便利的混雜各類元件的進行注入,這邊給各位開發者一個新的元件是透過外部配置做核心接口,稱為ImportSelector接口,在SpringBoot 中自動化配置和 @Enable相關的(功能性注解)核心原理都是它,此接口只有一個方法,返回需要的@Import的類別或相關Bean元件,透過這種方式我們可更加快速的一次注入性相當多的關聯元件,下面我們要來帶大家進一步討論。
Spring 框架所提供的導入選擇器目的在於提供一個或多個元件進行導入,他允許導入三種類型的程式碼,分別為Bean組態類型(@Configuration)、連結選擇器配置類型(@ImportSelector)及連結Bean定義註冊類型(@ImportBeanDefinitionRegistrar)三種類型,第一項Bean組態類型可包括所有元組件(@Component)擴展類型皆可整合,如:倉儲(@Repository)、服務(@Service)、元件(@Component)及組態(@Configuration)等註解模式,並逐一放入一個JSON格式數據集合,第二項連結選擇器配置類型,僅可在JSON格式數據中置放類別路徑字串,第三項Bean定義註冊類型,可在此層配置相關屬性值及相關Bean定義資料,常應用於啟動何種資料應用為主,如Spring @EnableXXX皆採用此種方式,開發者需包裝成列表元件(List)取得此外部配置的相關元件,相關範例我們可看以下程式碼。
範例一、導入選擇器範例程式碼,我們可將有定義或無定義元件的類別路徑放置入選擇導入器中,並在配置於組態內,提供各服務或相關測試進行運用。
//Set up ImportSelector
public class MarketImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[] {NorthMarketServiceImpl.class.getName(), SouthMarketServiceImpl.class.getName()};
}
}
//Import MarketImportSelector into Configuration
@Configuration
@Import({MarketImportSelector.class,AppConfig.class})
public class MarketConfiguration {
}
//Injection components into List
public class MarketSerivceTestSuite extends ServiceTestBase {
@Autowired
private List<MarketService> marketServices;
...
...
...
@Test
public void testPrintMarketArea() {
this.marketServices.forEach(MarketService::getMarketArea);
}
}
範例一測試結果,一次取得兩項MarketService並可印出結果
INFO 42455 --- [ Test worker] sw.spring.sample.MarketSerivceTestSuite : Started MarketSerivceTestSuite in 2.327 seconds (JVM running for 3.973)
Taiwan North
Taiwan South
範例二、加入@Configuration中的Bean組件,並組合成一個列表(List)
//配置Bean組件
@Configuration
public class AppConfig {
@Bean("WeistingMarket")
public MarketMdl getWeistingMarket() {
return new MarketMdl().setName("Weisting Market")
.setDescription("Created by Weisting.");
}
@Bean("JyunMarket")
public MarketMdl getJyunMarket() {
return new MarketMdl().setName("Jyun Market")
.setDescription("Created by Jyun.");
}
}
//Import App Config Bean into MarketConfiguration
@Configuration
@Import({MarketImportSelector.class,AppConfig.class})
public class MarketConfiguration {
}
//Test case
public class MarketSerivceTestSuite extends ServiceTestBase {
@Autowired
private List<MarketMdl> marketMdls;
@Test
public void testBeanContent() {
Assertions.assertTrue(context.containsBean("WeistingMarket"));
Assertions.assertTrue(context.containsBean("JyunMarket"));
marketMdls.forEach( marketMdl -> {
System.out.println(MessageFormat.format("Name : {0} , Description : {1}", marketMdl.getName(),marketMdl.getDescription()));
});
System.out.println("Verify Weisting and Jyun Bean success.");
}
}
範例二測試結果確認取得兩個Bean組件
INFO 42526 --- [ Test worker] sw.spring.sample.MarketSerivceTestSuite : Started MarketSerivceTestSuite in 2.371 seconds (JVM running for 3.978)
Name : Weisting Market , Description : Created by Weisting.
Name : Jyun Market , Description : Created by Jyun.
Verify Weisting and Jyun Bean success.
透過以上兩項簡單範例提供給各位開發者,小編認為這種相當適合運用條列式支援產品清單使用,亦可確認哪些產品以提供相關服務介接,可減少組態檔文件配置。
圖一 處理導入元件流程圖
根據上圖可知,Spring框架會先行初始化應用程式內容處理器,並進行BeanFactory配置池註冊流程,並過濾及處理相關組態流程,等待所有載入元件皆配置後,才開始進行判斷三項配置類型是否存在,分別為:ImportSelector、ImportBeanDefinitionRegistrar及@Configuration三項類型配置組件,並將其相關類別實例化,分別放置入集合中,當開發者進行注入對應類別物件,Spring框架會自動將對應類別物件整合成一個集合注入對應宣告欄位,透過以上介紹提供給各位讀者做參考。
Run test task
gradle test
Run open result html
open ./build/reports/tests/test/index.html
Import Selector test report
Mind-blowing selector import test description
Spring 源码分析之 ImportSelector 工作原理