1. 程式人生 > 其它 >Spring系列之@Import

Spring系列之@Import

技術標籤:springjavaspring

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見效。

總結下執行過程:

  1. 容器類註解@ComponentScan,故啟動時會掃描當前路徑
  2. 掃描到RootConfig類,由於RootConfig類註解@Import(OtherConfig.class),故掃描OtherConfig類
  3. RootConfig和OtherConfig中分別存在@Bean註解,會將@Bean註解的方法的返回值放入Spring容器中
  4. 容器啟動後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見效。

總結下執行過程:

  1. 容器類註解@ComponentScan,故啟動時會掃描當前路徑
  2. 掃描啟動類時發現@Import(MySelector.class),會取出MySelector類中selectImports方法的返回值,返回值為String陣列,陣列內容為類的全限定名
  3. 容器會載入這些類,我們示例中會載入MySelector
  4. 容器啟動後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見效。

總結下執行過程:

  1. 容器類註解@ComponentScan,故啟動時會掃描當前路徑
  2. 掃描啟動類時發現@Import(MyImportBeanDefinitionRegistrar.class),因為我們在MyImportBeanDefinitionRegistrar類中往BeanDefinition註冊器中添加了MyRegistrarBean的BeanDefinition
  3. 容器會載入這些類
  4. 容器啟動後MyRegistrarBean 在容器中,故可getBean拿到bean後使用
  5. 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));
        }
    }