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

 

posted @ 2020-07-27 21:06  Sniper_ZL  阅读(383)  评论(0编辑  收藏  举报