Spring @Import 机制

Spring @Import 机制

@Import 注解是 Spring 3.0 引入的一个新注解,用于 import Configuration,使用了这个注解,相当于 Java 中的 import 关键字,可以导入 Spring 的 bean 到 IOC 容器中。

Spring Boot 的自动装配就是基于 @Import 机制。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {

    Class[] value();

}

@Import 注解的解析

在简单的 demo 项目中,查看 @Import 注解的引用,除了作为注解使用,就只有在 ConfigurationClassParser 这个类中使用:

    /**
     * @param sourceClass 需要搜索的 class
       * @param imports 到目前为止已经找到的 imports
       * @param visited 去重,可能有些已经解析过了
     */
    private void collectImports(SourceClass sourceClass, Set imports, Set visited) throws IOException {
        if (visited.add(sourceClass)) {
            // 递归查找当前 class 的注解以及注解的注解中的 @Import
            for (SourceClass annotation : sourceClass.getAnnotations()) {
                String annName = annotation.getMetadata().getClassName();
                if (!annName.equals(Import.class.getName())) {
                    collectImports(annotation, imports, visited);
                }
            }
            // 添加当前 class 的 @Import 注解 import 进来的类
            imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
        }
    }

只有这个方法中对 @Import 注解进行了解析,内部逻辑就是递归查找当前类的注解,包括当前类的注解的注解(类似于 Java 中的继承),找到所有的 @Import 标注的 class 并且加到 imports 中。

@Import 的解析流程

查看调用 collectImports 的方法,发现就是 collectImports 上方的 getImports 方法,而 getImports 方法又在 doProcessConfigurationClass 中被调用,doProcessConfigurationClass 又被 processConfigurationClass 调用,processConfigurationClass 又在 parse 方法中调用,parseConfigurationClassPostProcessor 类的 processConfigBeanDefinitions 中被调用,继续查看是在 postProcessBeanDefinitionRegistrypostProcessBeanFactory 中被调用,通过 debug 发现是在 postProcessBeanDefinitionRegistry 中调用,再往上是 PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors,而这个方法是在 AbstractApplicationContext.invokeBeanFactoryPostProcessors 中被调用,整理整个流程如下图所示:

sequenceDiagram

AbstractApplicationContext ->> AbstractApplicationContext:refresh
AbstractApplicationContext ->> AbstractApplicationContext:invokeBeanFactoryPostProcessors
AbstractApplicationContext ->> PostProcessorRegistrationDelegate:invokeBeanFactoryPostProcessors
PostProcessorRegistrationDelegate ->> PostProcessorRegistrationDelegate:invokeBeanDefinitionRegistryPostProcessors
PostProcessorRegistrationDelegate ->> ConfigurationClassPostProcessor :postProcessBeanDefinitionRegistry
ConfigurationClassPostProcessor ->> ConfigurationClassPostProcessor:processConfigBeanDefinitions
ConfigurationClassPostProcessor ->> ConfigurationClassParser:parse
ConfigurationClassParser ->> ConfigurationClassParser:processConfigurationClass
ConfigurationClassParser ->> +ConfigurationClassParser:doProcessConfigurationClass
ConfigurationClassParser ->> ConfigurationClassParser:getImports
ConfigurationClassParser ->> -ConfigurationClassParser:processImports

获取到所有的 @Import 中的 value 之后,再执行 processImports 方法:

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
            Collection importCandidates, boolean checkForCircularImports) {

        if (importCandidates.isEmpty()) {
            return;
        }

        if (checkForCircularImports && isChainedImportOnStack(configClass)) {
            this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
        }
        else {
      // importStack 用来进行递归循环处理,可能当前处理的 import 中 import 进来了新的 import
            this.importStack.push(configClass);
            try {
        // 遍历所有被 import 的类,并根据所属类型执行对应的操作
        // 如果是 ImportSelector,执行 select 操作并递归处理
        // 如果是 ImportBeanDefinitionRegistrar,添加到当前 configClass 的 importBeanDefinitionRegistrars 中
        // 否则其他所有的都当做 Configuration 来处理,调用 processConfigurationClass
                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);
            // 
                        if (selector instanceof DeferredImportSelector) {
                            this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
                        }
                        else {
              // ImportSelector 返回的是类名数组,可能又返回了一个 ImportSelector,所以递归处理
                            String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                            Collection importSourceClasses = asSourceClasses(importClassNames);
                            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 {
                        // 当做 @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();
            }
        }
    }

常见的 @Import 用的 value 都是 ImportSelector 类型的,比如 @EnableAutoConfiguration 就用了 AutoConfigurationImportSelector,而在 AutoConfigurationImportSelector 中的 selectImports 方法仅仅是从 spring.factories 文件中读取 org.springframework.boot.autoconfigure.EnableAutoConfiguration 对应的 value 并做简单处理后返回,由 Spring 自己去处理这些 value,而开发者想要有自己的 EnableXxx 注解,在 /resources/META-INF/spring.factories 文件中添加如下信息,Spring Boot 就会加载这个 Xxx 类并交给 Spring 处理:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=Xxx

@Import 注解并不是 Spring Boot 独有的,但是 Spring Boot 借 @Import 机制来自动加载处理自动装配的类,进而实现自动装配。(ps: EnableXxx 模式也不是 Spring Boot 独有的,Spring Framework 中就已经有这样模式的使用了,Spring Boot 基于此进行扩展)

了解了 @Import 机制后,接下来继续深入 Spring Boot 的自动装配机制。

你可能感兴趣的:(Spring @Import 机制)