文章目錄
- 前言
- 一、導入普通類
- 二、導入ImportSelector實現類
- 三、導入ImportBeanDefinitionRegistrar實現類
- 四、@Import注解的解析
- 4.1、解析實現ImportSelector的候選bean
- 4.2、解析實現ImportBeanDefinitionRegistrar的候選bean
- 4.3、DeferredImportSelector的特殊處理
- 總結
前言
??@Import
是Spring框架提供的一個核心注解,主要用于在配置類中引入其他配置類或組件。通過在類上標注@Import
注解,可以將其value屬性中指定的類注冊到Spring容器中,從而實現配置的模塊化和靈活組合。
一、導入普通類
??@Import
注解會將其中導入的普通類,注冊成bean放入到Spring容器中。
public class Demo1 {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);System.out.println(context.getBean(MyService.class));}
}class MyService {
}@Configuration
@Import(MyService.class)
class Config {}
??運行結果:
com.itbaima.importdemo.demo1.MyService@55183b20
二、導入ImportSelector實現類
??解析@Import
注解時會執行實現了ImportSelector
的類的selectImports
方法,將返回的數組中的類注冊成bean。
public class Demo2 {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config1.class);System.out.println(context.getBean(MyService.class));}
}@Configuration
@Import(MyImport.class)
class Config1{}class MyImport implements ImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {return new String[]{"com.itbaima.importdemo.demo1.MyService"};}
}
??運行結果
com.itbaima.importdemo.demo1.MyService@33d512c1
三、導入ImportBeanDefinitionRegistrar實現類
??實現了ImportBeanDefinitionRegistrar
的類,可以自己注冊bean定義,以及從已有的bean定義中獲取指定的bean,進行修改,自由度是最高的。
public class Demo3 {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);System.out.println(context.getBean("orderService"));}
}@Configuration
@Import({MyBeanDefinitionRegistrar.class})
class Config {
}class MyBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) {GenericBeanDefinition bd = new GenericBeanDefinition();bd.setBeanClass(MyService.class);registry.registerBeanDefinition("orderService", bd);}
}class MyService {
}
??運行結果:
com.itbaima.importdemo.demo2.MyService@2f9f7dcf
四、@Import注解的解析
??@Import
注解的解析,體現在refresh方法中的invokeBeanFactoryPostProcessors
(調用所有bean工廠后置處理器)這一步:
??ConfigurationClassPostProcessor
,用于配置類的解析:
??在org.springframework.context.annotation.ConfigurationClassParser
的processImports
中完成對于@Import
注解的解析:
4.1、解析實現ImportSelector的候選bean
??對于候選bean實現了ImportSelector
接口的處理:
- 利用JVM的類加載機制,對于候選bean進行加載。
- 使用Spring提供的工具方法
instantiateClass
反射創建這個ImportSelector
實例,并注入必要的環境參數。 - 如果候選bean實現了
DeferredImportSelector
,則將其加入deferredImportSelectors
的集合中,延遲處理 - 候選bean實現的是
ImportSelector
接口,就調用目標類重寫的selectImports
方法,決定要導入哪些類名。然后把類名數組轉換成SourceClass
的集合,并應用排除過濾器。
??調用目標類重寫的selectImports
方法,傳入的參數就是加入了@Configuration
注解的配置類的元信息。最后一步遞歸調用processImports
方法很關鍵:假設當前第一次調用processImports
方法,解析出的類路徑是com.itbaima.importdemo.demo1.MyService,那么在遞歸調用processImports
方法時,再次進入
??循環中進行解析時,由于MyService沒有實現ImportSelector
或ImportBeanDefinitionRegistrar
注解,就會進入最后的else分支:(@Import導入的普通bean,進入的也是該分支)
??在最后的else分支中,還會去遞歸調用processConfigurationClass
,將MyService當做配置類去解析,最終將解析完成的放入到ConfigurationClass
的集合中。(注意,通過Import解析出的類路徑下的bean,bean名稱為空,后續需要再次進行處理。)
??this.reader.loadBeanDefinitions(configClasses);
這一步,會對于@Import導入的普通bean進行處理:
??生成一個beanName,并且將bean定義注冊到bean工廠中:
4.2、解析實現ImportBeanDefinitionRegistrar的候選bean
??@Import
導入實現ImportBeanDefinitionRegistrar
的候選bean的解析邏輯,在else…if的分支中,同樣是通過JVM的類加載機制,加載候選類的class類,然后使用Spring提供的工具方法instantiateClass
反射創建這個ImportBeanDefinitionRegistrar
實例,并注入必要的環境參數。
??最后將其加入到ConfigurationClass
的importBeanDefinitionRegistrars
屬性中:
??它的解析同樣是在processConfigBeanDefinitions
的this.reader.loadBeanDefinitions(configClasses);
中:
??回調用戶重寫的registerBeanDefinitions
的邏輯。
4.3、DeferredImportSelector的特殊處理
??用戶實現ImportSelector
時,有一種特殊的情況,即用戶實現了DeferredImportSelector
,DeferredImportSelector
是ImportSelector
的子類。在該分支中,僅僅是先將其加入DeferredImportSelectorHandler
的deferredImportSelectors
屬性中:
??DeferredImportSelectorHandler
的deferredImportSelectors
屬性是一個集合:
??最終會在所有bean解析完成后再去進行解析:
??該機制主要應用于Spring Boot的自動配置場景,其核心作用是確保用戶自定義的Bean能夠優先于自動配置的Bean執行。由于spring.factories中定義的自動配置Bean通常采用條件裝配機制,當容器中已存在用戶自定義的同類型Bean時,系統將不再重復裝配。這正是通過@Bean注解添加的Bean能夠覆蓋默認Bean的原因
??在doProcessConfigurationClass
中,默認的@Bean
的注解,是后于@Import
注解解析的。如果你不顯式允許覆蓋,Spring 在注冊 BeanDefinition 時會拋出異常或者忽略重復的注冊。
??但是對于DeferredImportSelector
的解析,是在doProcessConfigurationClass
的外層parse
方法中執行的,后于@Bean
注解的解析。
總結
??@Import注解是為了替換掉配置文件中的import標簽,主要是為了導入第三方的配置類,除此之外:
- 可以在配置類中指定@Import注解,將其中的類注入到容器中。
- 可以在配置類中指定@Import注解,同時其中的類實現了Import Selector接口,會執行重寫的selectImports方法,并且將其中指定的路徑的對象注入到容器中。
- 如果導入的類型實現了Import BeanDefinitionRegistrar,則可以自己注冊 BeanDefinition,自由度更高。