Spring源码阅读 - @Import 一个普通类

1. 解析逻辑

具体逻辑在 org.springframework.context.annotation.ConfigurationClassParser#processImports 中,下面的 importCandidates 即为 @Import 导入的类的列表。
最后的一个 else 即为普通类的处理逻辑,实际上就是将其当做一个配置类进行处理。
同时要注意到,在这里为止,@Import 导入的类还没有变为 BD 被注入容器,那么这个时机在哪

for (SourceClass candidate : importCandidates) {
    // Import 的类继承了 ImportSelector 接口
    if (candidate.isAssignable(ImportSelector.class)) {
        // ...
    }
    // 继承了 ImportBeanDefinitionRegistrar 接口,说明这个类有想自己向容器注入 BD 的想法,比如说 MyBatis 自己收集接口, 自己注入这些接口代理类的 BD
    else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
        // ........
    }
    else {
        // 普通类,这里的普通类包括 @Import 直接导入的没有继承上面三个接口的类,还有就是 @Import 导入了继承 ImportSelector 接口的类要注入的普通类
        // 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), exclusionFilter);
    }
}

2. 注入时机

从上面可见,被 @Import 的这些普通类并未被直接转换 BD 注入容器,仅是将这些类当做配置类处理一遍。
调用的处理配置类的方法为:org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass

protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
	// ... 略
    SourceClass sourceClass = asSourceClass(configClass, filter);
    do {
        // 这个是处理父子类的问题,一般注入子类,那么后面会查看是否有父类,父类会返回为 sourceClass,再解析 sourceClass
        //    但是配置类还是认为是最初的子类 configClass
        sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
    }
    while (sourceClass != null);

    this.configurationClasses.put(configClass, configClass);
}

可以看到这些被 @Import 的普通类虽然暂时没有被转换 BD 注入容器,但是被加入了一个集合。
下面转换到 ConfigurationClassPostProcessor 类中来,
方法:org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions

// ... 略
do {
    // 这里是配置类最初始的调用解析
    parser.parse(candidates);
    parser.validate();

    // 这里获取的就是上面那个集合
    Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
    // 去除已经解析过的,这个实际主要是用在后面 loadBeanDefinitions 去除不必要的 BD, removeAll 是为了减少判断
    //  alreadyParsed 是上一次循环已经解析过的,去除上一次解析过的就是这一次解析过的,主要是 Parser 是复用的,所以需要 removeAll
    configClasses.removeAll(alreadyParsed);

    // Read the model and create bean definitions based on its content
    if (this.reader == null) {
        this.reader = new ConfigurationClassBeanDefinitionReader(
                registry, this.sourceExtractor, this.resourceLoader, this.environment,
                this.importBeanNameGenerator, parser.getImportRegistry());
    }
    // 这里, 内部就讲这些 @Import 的普通类给注入容器了
    this.reader.loadBeanDefinitions(configClasses);
}

3. 注入逻辑

待续

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