@Import、@ImportAutoConfiguration 到底有什么區別?


 

背景

當我需要在一個Configuration配置類中導入另一個Configuration配置類時,我們可以使用 @Import注解 或者 @ImportAutoConfiguration ,如下兩圖:
經過試驗證明,從效果上來看,這兩種方式都能很好的工作,額外的配置類都被正確的加載了,並且都能觸發 ImportAware 擴展點,那么這兩個注解到底有啥區別呢?
 

解析

針對背景中的問題,我在網上搜索良久都沒有得到明確的滿意答案,還是決定通過源碼來窺探一下其中奧秘。
如果時間緊任務急的同學也可以直接看最后的總結

 

原理剖析

通過兩個注解的元信息,我們可以看到@ImportAutoConfiguration 注解中繼承了一個@Import 注解。
 
所以想要搞清楚這兩者的區別,我們必須先搞明白 Import 注解是個什么東西。
 

@Import

先貼一個Import注解類的注釋信息 (附上有道翻譯版本。。)
表示要導入的一個或多個@Configuration類。
提供與Spring XML中的<import/>元素等價的功能。允許導入@Configuration類、ImportSelector和ImportBeanDefinitionRegistrar實現,以及常規的組件類(從4.2;類似於AnnotationConfigApplicationContext.register)。
在導入的@Configuration類中聲明的@Bean定義應該通過@Autowired注入來訪問。可以自動連接bean本身,也可以自動連接聲明該bean的配置類實例。后一種方法允許在@Configuration類方法之間進行顯式的、ide友好的導航。
可以在類級別聲明,也可以作為元注釋聲明。
如果需要導入XML或其他non-@Configuration bean定義資源,則使用@ImportResource注釋。
 
大概意思就是說,Import 注解主要是用來替代 Spring XML 中的<import/>標簽的,我們來看下<import/>標簽又是干嘛的呢?
根據官方文檔的說法,import是用來解決配置混亂的問題的,它支持我們將需要聲明的bean進行分類並寫入多個xml配置文件中,並通過import標簽將這些配置文件整合到一起,是配置文件邏輯更加清晰,也更加優雅。
 
大家知道@Configuration類本質上就是一個xml配置文件的替代品,現在我們知道了,@Import 注解的初衷就是使我們可以在配置類中導入其他配置類,並最終將這些配置類整合在一起進行處理。
 
除此之外,@Import注解的注釋中寫了,除了導入@Configuration配置類以外,它還能導入ImportSelector 和 ImportBeanDefinitionRegistrar 實現類,那么 ImportSelector和ImportBeanDefinitionRegistrar 是什么東西呢。
 
正如上面圖片所示的那樣,直接在 Import 注解中聲明配置類的方式是最常用的姿勢,不過這是有缺陷的,比如:
  1. 導入的配置類集合是固定的,無法根據實際場景進行選擇性的導入
  2. 由於Import注解的value只接受 Class 類型的,意味着所導入的類必須在當前模塊或所依賴的下游模塊中,無法導入一個上游 或 第三方的配置類(類似SPI的機制,雖然這不是很好理解,也很少會有這種需求。。。)
 
ImportSelector 就可以解決上面的兩個缺陷,它根據導入的ImportSelector實現類所返回的值作為需要導入的 配置類集合,並對這些配置類進行加載。
我們考慮這樣一個需求
如果應用配置中指定了使用1號配置類時,加載 MyOtherConfiguration1.java,否則默認加載 MyOtherConfiguration2.java
針對這個場景,我們可以通過以下配置方式進行實現:
 
相比 ImportSelector 這種返回類全限定名集合來指定需要加載的配置類的方式,ImportBeanDefinitionRegistrar 則更加直接,它的接口中額外提供了 BeanDefinitionRegistry 參數,意味着我們可以在 ImportBeanDefinitionRegistrar 的實現類中自主的進行 Bean的注冊,而不是返回一個類名或 類對象。
我們依然考慮這樣一個需求
如果應用配置中指定了使用1號配置類時,加載 MyOtherConfiguration1.java,否則默認加載 MyOtherConfiguration2.java
針對這個場景,我們可以通過以下配置方式進行實現:
 
至此,我們算是搞清楚了 @Import 注解的工作模式,關於它的工作機制與原理,如果大家感興趣,可以對源碼進行窺探,相關邏輯入口在 org.springframework.context.annotation.ConfigurationClassParser:302行
 

@ImportAutoConfiguration

接下來輪到 @ImportAutoConfiguration 注解了
定睛一看,@ImportAutoConfiguration 繼承了 @Import 注解,並導入了一個 ImportSelector 的實現類 ImportAutoConfigurationImportSelector。
根據我們剛才的了解
Import注解 根據導入的ImportSelector實現類所返回的值作為需要導入的 配置類集合,並對這些配置類進行加載。
我們可以猜到 ImportAutoConfigurationImportSelector 肯定是根據某種邏輯返回了需要被導入的配置類集合。
我們發現 ImportAutoConfigurationImportSelector 並沒有實現 selectImports 接口,這個接口是由它的父類 AutoConfigurationImportSelector 進行實現的。
可以看到它是調用 getAutoConfigurationEntry 獲取配置類集合,繼續看 getAutoConfigurationEntry 的邏輯
可以看到它調用了 getCandidateConfigurations 方法獲取的配置類集合,就是這個方法被 ImportAutoConfigurationImportSelector 重寫了邏輯,如下:
簡單解釋一下它的邏輯
  1. 先獲取當前配置類上的 ImportAutoConfiguration 注解信息
  2. 如果 ImportAutoConfiguration 注解中指定了classes(或value) 屬性值,那么直接返回 classes 的值
  3. 如果未配置 classes(或value) 屬性值,從 spring.factories 文件中讀取key為當前配置類名的結果
 
配合一個案例場景可能更加好理解
在 MyAutoConfiguration 配置類中 導入 MyOtherAutoConfiguration
通過 @ImportAutoConfiguration 注解可以有兩種方式實現這個需求
方式一:直接在注解中指定
方式二:注解中不指定classes或value,並在 spring.factories 中指定目標配置類
 

總結

相同點

  1. Import 與 ImportAutoConfiguration 注解都可以通過 value 屬性指定需要導入的@Configuration配置類
  2. Import 與 ImportAutoConfiguration 注解都支持通過 value 屬性配置 ImportSelector類、ImportBeanDefinitionRegistrar類 的方式來間接導入所需的配置類

不同點

  1. @Import 注解只能通過 value 屬性值進行導入
  2. @ImportAutoConfiguration 注解的 value 屬性值是可選的,當value屬性值未指定時,會嘗試從 spring.factories 文件中讀取相應的配置作為需要導入的配置類集合
 

彩蛋

你知道 @ImportAutoConfiguration 和 @EnableAutoConfiguration 有什么區別嗎?
可以看到很熟悉的一幕,@EnableAutoConfiguration注解繼承了 @Import 注解並加載了一個
AutoConfigurationImportSelector,這不就是剛剛 ImportAutoConfigurationImportSelector 的父類嗎?
我們看下沒有被 ImportAutoConfigurationImportSelector 重寫過的邏輯是怎么樣的
它直接去 spring.factories 配置文件中尋找 EnableAutoConfiguration 配置項來作為配置類集合進行加載,如下:
 
那 @EnableAutoConfiguration 在哪里被使用的呢?
@SpringBootApplication 注解繼承了 EnableAutoConfiguration 注解。。。
 
那么。。好像發現了 SpringBoot 自動裝配的秘密呢 ~~
 
 


免責聲明!

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



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