Spring系列之@Import
阿新 • • 發佈:2021-02-07
Spring系列之@Import
@Import簡介
@Import表示用來匯入配置類或者一些需要前置載入的類.。
@Import支援 三種方式
1.帶有@Configuration的配置類(4.2 版本之前只可以匯入配置類, 4.2版本之後 也可以匯入 普通類)
2.ImportSelector 的實現
3.ImportBeanDefinitionRegistrar 的實現
匯入@Configuration的配置類
1、程式碼結構見上圖,RootConfig和OtherConfig在兩個目錄中
//ComponentScan包掃描,預設掃描當前路徑下所有檔案和子目錄檔案
//所有會掃描到RootConfig而掃描不到OtherConfig
@ComponentScan
public class SpringApplicationContext {
public static void main(String[] args) {
//建立Annotation上下文
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringApplicationContext.class);
//得到AService的bean
AService bean = context.getBean(AService.class);
bean.service();
//得到BService的bean
BService bean2 = context.getBean(BService.class);
bean2.service();
//關閉容器
context.close();
}
}
2、先看RootConfig 和AService程式碼
//Configuration等同於xml配置
//類中@bean註解的會被註冊到spring容器中
//@import 將另外一個Configuration匯入到當前Configuration中
//等於xml中<import>另一個xml
@Configuration
@Import(OtherConfig.class)
public class RootConfig {
@Bean
public AService orderDemoService(){
return new AService();
}
}
//AService 無任何註解,表示沒有交給Spring管理
public class AService {
public void service(){
System.out.println("AService run");
}
}
3、再看OtherConfig 和BService程式碼
//Configuration等同於xml配置
//類中@bean註解的會被註冊到spring容器中
@Configuration
public class OtherConfig {
@Bean
public BService omsService(){
return new BService();
}
}
//BService 無任何註解,表示沒有交給Spring管理
public class BService {
public void service(){
System.out.println("BService run");
}
}
4、最後看執行效果
執行結果表示,AService和BService 都是spring的bean,交給容器管理,說明此處import見效。
總結下執行過程:
- 容器類註解@ComponentScan,故啟動時會掃描當前路徑
- 掃描到RootConfig類,由於RootConfig類註解@Import(OtherConfig.class),故掃描OtherConfig類
- RootConfig和OtherConfig中分別存在@Bean註解,會將@Bean註解的方法的返回值放入Spring容器中
- 容器啟動後AService和BService都在容器中,故可getBean拿到bean後使用
匯入ImportSelector 的實現
1、先看啟動類
//匯入ImportSelector的實現類
//面向介面程式設計
@ComponentScan
@Import(MySelector.class)
public class SpringApplicationContext {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringApplicationContext.class);
//得到MyBean的bean
MyBean myBean = context.getBean(MyBean.class);
myBean.service();
//關閉容器
context.close();
}
}
2、繼續上MySelector 和MyBean程式碼
//實現ImportSelector介面中selectImports方法
public class MySelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{MyBean.class.getName()};
}
}
//MyBean 無任何註解,表示沒有交給Spring管理
public class MyBean {
public void service(){
System.out.println("MyBean run");
}
}
3、最後看執行結果
執行結果表示,MyBean在容器中,說明此處import見效。
總結下執行過程:
- 容器類註解@ComponentScan,故啟動時會掃描當前路徑
- 掃描啟動類時發現@Import(MySelector.class),會取出MySelector類中selectImports方法的返回值,返回值為String陣列,陣列內容為類的全限定名
- 容器會載入這些類,我們示例中會載入MySelector
- 容器啟動後MySelector在容器中,故可getBean拿到bean後使用
ImportBeanDefinitionRegistrar 的實現
1、先看啟動類
//匯入ImportBeanDefinitionRegistrar的實現類
//可批量加入bean
@ComponentScan
@Import(MyImportBeanDefinitionRegistrar.class)
public class SpringApplicationContext {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringApplicationContext.class);
//得到類的例項
MyRegistrarBean myRegistrarBean = context.getBean(MyRegistrarBean.class);
myRegistrarBean.service();
//關閉容器
context.close();
}
}
2、繼續上MyImportBeanDefinitionRegistrar和MyRegistrarBean程式碼
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
String beanName = MyRegistrarBean.class.getSimpleName();
//beanName轉化為駝峰式
beanName = StringUtils.uncapitalize(beanName);
//新建RootBeanDefinition放入BeanDefinition註冊器中
RootBeanDefinition beanDefinition = new RootBeanDefinition(MyRegistrarBean.class);
registry.registerBeanDefinition(beanName,beanDefinition);
}
}
public class MyRegistrarBean {
public void service(){
System.out.println("MyRegistrarBean service");
}
}
3、最後看執行結果
執行結果表示,MyRegistrarBean 在容器中,說明此處import見效。
總結下執行過程:
- 容器類註解@ComponentScan,故啟動時會掃描當前路徑
- 掃描啟動類時發現@Import(MyImportBeanDefinitionRegistrar.class),因為我們在MyImportBeanDefinitionRegistrar類中往BeanDefinition註冊器中添加了MyRegistrarBean的BeanDefinition
- 容器會載入這些類
- 容器啟動後MyRegistrarBean 在容器中,故可getBean拿到bean後使用
- ImportBeanDefinitionRegistrar因為直接新增BeanDefinition,故更強大靈活,也對開發者有一定的spring基礎要求
總結
1、我們在springboot中經常見到匯入ImportSelector和ImportBeanDefinitionRegistrar的程式碼,用的就是這個原理。
2、在日常開發中,我們可以通到配置XML或者外部資源配置管理類全限定名,起到手動載入自己想要的類的目的。
3、ImportSelector原始碼實現是在初始化beanFactory時完成解析,原始碼較複雜,有興趣可自己檢視。
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, boolean checkForCircularImports) throws IOException {
//刪除無關程式碼
// Import 註解中配置的是 ImportSelector 型別
if (candidate.isAssignable(ImportSelector.class)) {
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class); // 例項化
ParserStrategyUtils.invokeAwareMethods(selector, this.environment, this.resourceLoader, this.registry); // 如果該類有實現對應的 Aware 介面,則注入對應的屬性
if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) { // DeferredImportSelector 型別的放到集合中待後續處理
this.deferredImportSelectors.add(new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
}
else { // 直接呼叫 selectImports 方法取得對應的類
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// 如果是 ImportBeanDefinitionRegistrar 型別
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar = BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(registrar, this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// 如果非上面兩種型別,那麼代表這是一個與 @Configuration 相關的類
this.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass));
}
}