1. 程式人生 > 實用技巧 >BeanFactory後置處理器 - ConfigurationClassPostProcessor - Import

BeanFactory後置處理器 - ConfigurationClassPostProcessor - Import

開始閱讀 Import 原始碼之前, 也是需要些一些測試demo, 來幫助理解和除錯

demo

建幾個內容一樣的類, 如: IndexDao1, IndexDao2, IndexDao3

其具體內容如下:

public class IndexDao1 {

    public IndexDao1() {
        System.out.println("IndexDao1 -- constructor");
    }

    private String name = "IndexDao1";

    public String getName() {
        return
name; } public void setName(String name) { this.name = name; } }

配置類:

@EnableIndexRegistrar
@EnableIndex
@Import(IndexDao3.class)
public class StartConfig {}

註解類:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(IndexSelector.class)
public @interface
EnableIndex { boolean open() default true; }
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(IndexRegistrar.class)
public @interface EnableIndexRegistrar {
    boolean open() default true;
}

selector:

public class IndexRegistrar implements ImportBeanDefinitionRegistrar
{ @Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AnnotationAttributes annAttr = AnnotationAttributes.fromMap( importingClassMetadata.getAnnotationAttributes(EnableIndexRegistrar.class.getName()) ); boolean open = (boolean) annAttr.get("open"); if(!open){ return; } BeanDefinition bd = new RootBeanDefinition(IndexDao2.class); registry.registerBeanDefinition("indexDao2", bd); } }
public class IndexSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        AnnotationAttributes annAttr = AnnotationAttributes.fromMap(
                importingClassMetadata.getAnnotationAttributes(EnableIndex.class.getName())
        );
        boolean open = (boolean) annAttr.get("open");
        if(!open){
            return new String[0];
        }

        return new String[]{IndexDao1.class.getName()};
    }
}

測試方法:

public static void main(String[] args) {
    AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext(StartConfig.class);
    IndexDao1 bean1 = acac.getBean(IndexDao1.class);
    IndexDao2 bean2 = acac.getBean(IndexDao2.class);
    IndexDao3 bean3 = acac.getBean(IndexDao3.class);
}

結果:

這個demo中, 我使用了 selector, registrar, 普通類的方式, 分別讓把 IndexDao1, IndexDao2, IndexDao3 交給spring了.

通過 import 這種方式, 可以控制載入一些元件, 如 mybatis, EnableCaching

原始碼

接著之前的org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass

這個方法中呼叫了 processImports , 這個是用來處理 Import 的.

/**
 * 處理@Import  imports 3種情況
 * ImportSelector  將類的字串陣列返回給spring, 這些類的建立過程, 完全由 spring 去解析建立, 經典示例: @EnableCaching
 * 普通類   普通類會生成 db, 放到 Map<ConfigurationClass, ConfigurationClass> configurationClasses 中, 
* 等待 parse 方法執行完後, 註冊到 spring 容器中, 由 spring 去建立 * ImportBeanDefinitionRegistrar 使用者建立(或掃描建立) bd, 然後將這些bd註冊到容器中, 由spring去建立, 經典示例: mybatis
*/ private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, boolean checkForCircularImports) { if (importCandidates.isEmpty()) { return; } if (checkForCircularImports && isChainedImportOnStack(configClass)) { this.problemReporter.error(new CircularImportProblem(configClass, this.importStack)); } else { this.importStack.push(configClass); try { for (SourceClass candidate : importCandidates) { if (candidate.isAssignable(ImportSelector.class)) { // Candidate class is an ImportSelector -> delegate to it to determine imports Class<?> candidateClass = candidate.loadClass(); ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class); ParserStrategyUtils.invokeAwareMethods( selector, this.environment, this.resourceLoader, this.registry); //判斷是否是 延遲Import, 這個不用管 if (selector instanceof DeferredImportSelector) { this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector); } else { //拿到 import 返回的類的字串陣列 String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); //將字串解析為 SourceClass, 裡面通過 ClassUtils.forName 的方式, 載入類, 再封裝為 SourceClass Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames); //檢測 返回的類中, 是否還有 @Import, 這裡需要遞迴, 因為不能確定出來的類中, 是否還有@Import processImports(configClass, currentSourceClass, importSourceClasses, false); } } else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) { // Candidate class is an ImportBeanDefinitionRegistrar -> // delegate to it to register additional bean definitions 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 { // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar -> // process it as an @Configuration class this.importStack.registerImport( currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); processConfigurationClass(candidate.asConfigClass(configClass)); } } } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to process import candidates for configuration class [" + configClass.getMetadata().getClassName() + "]", ex); } finally { this.importStack.pop(); } } }

先看一下, ImportSelector 和ImportBeanDefinitionRegistrar 裡面分別是什麼

ImportSelector

public interface ImportSelector {
    String[] selectImports(AnnotationMetadata importingClassMetadata);
}

ImportSelector 返回的, 其實是 類的名稱的字串陣列.

ImportBeanDefinitionRegistrar

public interface ImportBeanDefinitionRegistrar {
    void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}

這個介面沒有返回值, 但是提供了一個註冊器. 我們可以在註冊器中, 註冊 bd

上面這個processImports方法中, 呼叫了 ImportSelector 的 selectImports 方法, 但是卻沒有呼叫ImportBeanDefinitionRegistrar 的方法.

說明她的方法不是在這裡呼叫的. 那麼他是在哪裡呼叫的呢?

org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions 中的

this.reader.loadBeanDefinitions(configClasses);

其呼叫棧:

org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass

  org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsFromRegistrars

    org.springframework.context.annotation.ImportBeanDefinitionRegistrar#registerBeanDefinitions

普通類:

普通類的時候, 不會呼叫什麼介面, 但是會對這個普通類進行解析, 生成 bd