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) 时会被解析并注入容器。

posted @ 2022-04-09 22:30  YangDanMua  阅读(53)  评论(0编辑  收藏  举报