• Spring源码阅读 @Import 一个继承 ImportSelector 接口的类


    处理逻辑在 org.springframework.context.annotation.ConfigurationClassParser#processImports
    暂时略其子接口 DeferredImportSelector

    1. ImportSelector 接口

    selectImports 方法返回的字符串数组,每个字符串被视为一个要被注入容器的类的全限定名。

    public interface ImportSelector {
    
        /**
         * Select and return the names of which class(es) should be imported based on
         * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
         * @return the class names, or an empty array if none
         */
        String[] selectImports(AnnotationMetadata importingClassMetadata);
    
        /**
         * Return a predicate for excluding classes from the import candidates, to be
         * transitively applied to all classes found through this selector's imports.
         * <p>If this predicate returns {@code true} for a given fully-qualified
         * class name, said class will not be considered as an imported configuration
         * class, bypassing class file loading as well as metadata introspection.
         * @return the filter predicate for fully-qualified candidate class names
         * of transitively imported configuration classes, or {@code null} if none
         * @since 5.2.4
         */
        @Nullable
        default Predicate<String> getExclusionFilter() {
            return null;
        }
    
    }
    

    2. 处理逻辑

    先实例化这个被导入的类,然后执行 selectImports,根据全限定名数组加载 class 数组,并将这些 class 当做配置类解析一遍。
    注意到这个被导入的类直接被实例化了,BD 没有注入容器,实例也没有注入容器。
    问题: 导入的 class 数组被当做配置类解析,但是没有被注入容器啊,注入容器的时机在哪?

    // Import 的类继承了 ImportSelector 接口
    if (candidate.isAssignable(ImportSelector.class)) {
        // Candidate class is an ImportSelector -> delegate to it to determine imports
        Class<?> candidateClass = candidate.loadClass();
        // 实例化这个类
        ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
                this.environment, this.resourceLoader, this.registry);
        Predicate<String> selectorFilter = selector.getExclusionFilter();
        if (selectorFilter != null) {
            exclusionFilter = exclusionFilter.or(selectorFilter);
        }
        // 继承了 DeferredImportSelector 接口(ImportSelector 的子接口)
        if (selector instanceof DeferredImportSelector) {
            // ...
        }
        else {
            // 否则立即处理,将返回的字符串数组当做类全限定名数组,加载对应的 class 入内存
            // 传入的信息是使用了 @Import 注解的类的信息
            String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
            Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
            // 把这些加载的类当做直接 @Import 的类进行处理,注意到原 @Import 的类并未被注入容器
            processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
        }
    }
    

    3. BD 注入时机

    注意到上面讲 selector.selectImports 要注入的类重新调用 processImports 处理了,那么这些类和被配置类直接 @Import 没有什么区别,也意味着若这个类是普通类, 那么走普通类@Import 的逻辑,继承了 ImportSelector 接口则走当前的处理逻辑,也意味着这个类不会被注入容器。
    若是普通类,则和和 @Impoprt 导入普通类一样,需要等到 ConfigurationClassPostProcessor 显式调用 this.reader.loadBeanDefinitions(configClasses) 时会被解析并注入容器。

  • 相关阅读:
    MySQL-基础知识整理
    设计模式-适配器模式
    MySQL
    MySQL-5.7 填坑
    MySQL
    Oracle
    SQL 注入
    Apache JMeter
    AppScan
    DNS 搜索
  • 原文地址:https://www.cnblogs.com/chenxingyang/p/16124032.html
Copyright © 2020-2023  润新知