• BeanFactory后置处理器


    开始阅读 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

  • 相关阅读:
    苹果快速的修复了Mac OS High Sierra 上出现了root的漏洞
    Codeforces Round #525 (Div. 2) C. Ehab and a 2-operation task
    2018CHD-ACM新生赛(正式赛)E.解救迷茫的草滩小王子
    2018CHD-ACM新生赛(正式赛)D.刀塔大师lwq I
    2018CHD-ACM新生赛(正式赛)C.绝望のRevue
    最小生成树——克鲁斯克算法+一道例题
    求连通分量个数+判定二分图
    动态规划——滚动数组(省内存)
    [BZOJ 1491] [NOI 2007] 社交网络
    SPOJ 8222 Substrings 后缀自动机
  • 原文地址:https://www.cnblogs.com/elvinle/p/13245846.html
Copyright © 2020-2023  润新知