【SpringBoot】【自动装配】 SpringBoot自动装配原理

1  前言

我们都知道 SpringBoot 有个自动装配的机制,那你们知道平时如何使用么,以及他是什么时候执行的么,那么本节我们就来看看。

2  为什么要有自动装配

我们经历过SSM时期的时候,比如要引进Redis,是不是要先要导Maven依赖,还要进行大量的配置Bean,然后才能使用,而使用 SpringBoot 的方式,我们只需要引入一个 starter,然后配置redis信息,就可以使用了,这正是因为自动装配替我们做了繁琐的事。

3  自动装配不得不说的注解

说到自动装配原理,就要先看个注解 @Import,它的作用是什么呢?就是用来导入配置类或者一些需要前置加载的类,加载进我们的 Spring容器中。

可以看到该注解可以导入的三种类型:Configuration, ImportSelector, ImportBeanDefinitionRegistrar,用于注册某个类。

为什么要说这个注解呢?

因为自动装配入口就是通过 @Import 引入了 AutoConfigurationImportSelector 类,也就是自动装配的入口,然后 AutoConfigurationImportSelector 去帮我们完成一些类的装配也就是加载进容器里,才让我们更方便的去使用。 所以我们接下来就要看看 @Import 是什么时候执行的以及 AutoConfigurationImportSelector 又是如何解析执行的。

再另说一个小细节,我们启动类上的 @SpringBootConfiguration,其实他里边也是一个 @Configuration,为什么要说这个细节呢?因为下边提到的后置处理器会对配置类进行解析分析,所以我们的启动类里的 @Import继而被分析到呀。

4  @Import 解析执行时机

执行时机我先说一下是通过一个Bean工厂的后置处理器:ConfigurationClassPostProcessor 去解析执行的,那么该后置处理器又是何时执行的呢?就是 SpringBoot启动的时候刷新上下文的时候:

好了,那我们知道是刷新上下文的时候,通过执行Bean工厂的后置处理器来解析 @Import 继而执行的,那我们来看看 ConfigurationClassPostProcessor 的细节。

4.1  ConfigurationClassPostProcessor 继承关系

可以看到 ConfigurationClassPostProcessor 实现了 BeanDefinitionRegistryPostProcessor 接口,那我们接着来看看 postProcessBeanDefinitionRegistry 方法。

4.2  postProcessBeanDefinitionRegistry 处理

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    // 生成一个id,防止重复执行
    int registryId = System.identityHashCode(registry);
    // 若重复就抛出异常
    if (this.registriesPostProcessed.contains(registryId)) {
        throw new IllegalStateException(
                "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
    }
    if (this.factoriesPostProcessed.contains(registryId)) {
        throw new IllegalStateException(
                "postProcessBeanFactory already called on this post-processor against " + registry);
    }
    // 表示此registry里的bd收集动作,已经做了  避免再重复收集此registry
    this.registriesPostProcessed.add(registryId);
    // 根据配置类,收集到所有的bd信息 也是@Import解析的入口时机  因为我们的启动类上也是一个@Configuration
    processConfigBeanDefinitions(registry);
}

我们继续进去 processConfigBeanDefinitions 方法:

/**
 * Build and validate a configuration model based on the registry of
 * {@link Configuration} classes.
 */
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
    // 获取此时注册的bean
    String[] candidateNames = registry.getBeanDefinitionNames();
    // 遍历当前容器中所有的BeanDefinition,从中寻找有@Configuration的类
    for (String beanName : candidateNames) {
        BeanDefinition beanDef = registry.getBeanDefinition(beanName);
        if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
            if (logger.isDebugEnabled()) {
                logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
            }
        }
        // 判断是否有@Configuration注解,如果有就加入候选列表
        else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
            configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
        }
    }
    // 如果候选列表为空,就直接返回了 Return immediately if no @Configuration classes were found
    if (configCandidates.isEmpty()) {
        return;
    }
    // 如果使用了@Order注解,就排个序 Sort by previously determined @Order value, if applicable
    configCandidates.sort((bd1, bd2) -> {
        int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
        int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
        return Integer.compare(i1, i2);
    });
    // 自定义的bean名称生成策略 Detect any custom bean name generation strategy supplied through the enclosing application context
    SingletonBeanRegistry sbr = null;
    if (registry instanceof SingletonBeanRegistry) {
        sbr = (SingletonBeanRegistry) registry;
        if (!this.localBeanNameGeneratorSet) {
            BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
                    AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
            if (generator != null) {
                this.componentScanBeanNameGenerator = generator;
                this.importBeanNameGenerator = generator;
            }
        }
    }
    if (this.environment == null) {
        this.environment = new StandardEnvironment();
    }
    // 下面开始解析候选列表
    // 创建一个ConfigurationClassParser,后面的解析将委托给这个对象
    // 创建配置类的解析器 Parse each @Configuration class
    ConfigurationClassParser parser = new ConfigurationClassParser(
            this.metadataReaderFactory, this.problemReporter, this.environment,
            this.resourceLoader, this.componentScanBeanNameGenerator, registry);
    Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
    Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
    do {
        StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
        // 真正解析的入口,在这儿会对一系列注解进行解析如@Component,@import,@ComponentScan等
        parser.parse(candidates);
        parser.validate();
        Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
        configClasses.removeAll(alreadyParsed);
        // 如果BeanDefinitionReader为空,则创建一个,BeanDefinitionReader是用来加载BeanDefinition
        // 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());
        }
        // 前面ConfigurationClassParser#parse处理时遗留了三类类型,只将其解析并保存,并没有注册成BeanDefinition,在这个方法中进行处理
        // 使用BeanDefinitionReader进行加载BeanDefinition
        this.reader.loadBeanDefinitions(configClasses);
        alreadyParsed.addAll(configClasses);
        processConfig.tag("classCount", () -> String.valueOf(configClasses.size())).end();
        candidates.clear();
        // 再次检查是否有遗漏的配置类未解析,若存在继续执行while循环
        if (registry.getBeanDefinitionCount() > candidateNames.length) {
            // 如果发现当前容器中的BeanDefinition数量比上一轮解析完以后的数量多,说明这一轮解析了新的BeanDefinition
            // 这种情况需要对BeanDefinition列表逐个判断,如果其类型加了@Configuration注解,那么需要放入候选列表中,进行下一轮解析
            String[] newCandidateNames = registry.getBeanDefinitionNames();
            Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
            Set<String> alreadyParsedClasses = new HashSet<>();
            for (ConfigurationClass configurationClass : alreadyParsed) {
                alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
            }
            for (String candidateName : newCandidateNames) {
                if (!oldCandidateNames.contains(candidateName)) {
                    BeanDefinition bd = registry.getBeanDefinition(candidateName);
                    if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
                            !alreadyParsedClasses.contains(bd.getBeanClassName())) {
                        candidates.add(new BeanDefinitionHolder(bd, candidateName));
                    }
                }
            }
            candidateNames = newCandidateNames;
        }
    }
    while (!candidates.isEmpty());
    // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
    if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
        sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
    }
    if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
        // Clear cache in externally provided MetadataReaderFactory; this is a no-op
        // for a shared cache since it'll be cleared by the ApplicationContext.
        ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
    }
}

整体的一个执行流程就是:

  • 筛选获取配置类,对配置类进行排序操作
  • 初始化Bean名称生成器
  • 解析并注册所有的配置类
  • 注册ImportRegistry
  • 清理缓存

在解析和加载 BeanDefinition 时,需要通过特定的规则进行扫描, 主要看 ConfigurationClassParser#parse和 ConfigurationClassBeanDefinitionReader#loadBeanDefinitions 两个方法。 

4.3  parse 解析配置类

我们先来看下 parse 方法:

/**
 * 根据当前bean的类型去解析,不过最终都会进入processConfigurationClass方法
 * @param configCandidates 配置类集合
 */
public void parse(Set<BeanDefinitionHolder> configCandidates) {
    // 依次解析所有候选BeanDefinition
    for (BeanDefinitionHolder holder : configCandidates) {
        BeanDefinition bd = holder.getBeanDefinition();
        try {
            // 不同的bean定义类型执行不同的parse 方法
            if (bd instanceof AnnotatedBeanDefinition) {
                parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
            }
            else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
                parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
            }
            else {
                parse(bd.getBeanClassName(), holder.getBeanName());
            }
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(
                    "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
        }
    }
    // 处理前面在解析@Import注解中为DeferredImportSelector子类的对象 这儿也就是SpringBoot自动配置的入口
    // 因为我们的 AutoConfigurationImportSelector 就是实现的 DeferredImportSelector 因为自动配置要被创建一般会有前提条件,所以推迟在这里处理
    // public class AutoConfigurationImportSelector implements DeferredImportSelector
    this.deferredImportSelectorHandler.process();
}

根据不同的 BeanDefiniton 进行解析:

继续深入看  parse 方法,最后其实都会走到 processConfigurationClass 方法里:

4.3.1  processConfigurationClass 方法

那么看一下processConfigurationClass方法的实现:

protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
    // 如果当前类使用了@Conditional注解,则需要根据条件判断是否要跳过该类的解析
    if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
        return;
    }
    ConfigurationClass existingClass = this.configurationClasses.get(configClass);
    //判断当前类是否已经解析过,防止重复解析
    if (existingClass != null) {
        if (configClass.isImported()) {
            if (existingClass.isImported()) {
                //如果是被@Import注解导入的,那么记录一下
                existingClass.mergeImportedBy(configClass);
            }
            // 直接结束了,不需要重复解析 Otherwise ignore new imported config class; existing non-imported class overrides it.
            return;
        }
        else {
            // 如果当前类不是被其他类通过@Import注解导入的,说明其是被显式定义的(说明一个类被定义了两次),那么将旧的移除。(重新解析一次)
            // Explicit bean definition found, probably replacing an import.
            // Let's remove the old one and go with the new one.
            this.configurationClasses.remove(configClass);
            this.knownSuperclasses.values().removeIf(configClass::equals);
        }
    }
    // Recursively process the configuration class and its superclass hierarchy.
    SourceClass sourceClass = asSourceClass(configClass, filter);
    do {
        // 进行递归解析,直到该类没有父类为止
        sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
    }
    while (sourceClass != null);
    this.configurationClasses.put(configClass, configClass);
}

整体的执行过程大致是:

  • 判断当前类是否需要解析,判断委托给了ConditionEvaluator类进行处理,这个类型是根据当前类的@Conditional注解进行处理的。

  • 判断当前类是否已经被加载过,如果是被@Import依赖的,那么记录一下就直接返回不重复处理了;如果不是被@Import依赖的,那么就再解析一遍(覆盖上一次的解析结果)。

  • 具体解析的调用,是调用doProcessConfigurationClass方法进行处理,可以发现这个方法被一个循环所包围,因为方法会返回当前类型的父类,如果其父类存在,则会循环解析,知道不存在父类时,会返回null

4.3.2  doProcessConfigurationClass 方法

那么看一下doProcessConfigurationClass方法的实现, 这个是具体处理 Configuration 的核心方法:

@Nullable
protected final SourceClass doProcessConfigurationClass(
        ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
        throws IOException {
    // 处理@Component注解
    if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
        // 递归处理当前类的内部类 Recursively process any member (nested) classes first
        processMemberClasses(configClass, sourceClass, filter);
    }
    // 处理@PropertySource注解 Process any @PropertySource annotations
    for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
            sourceClass.getMetadata(), PropertySources.class,
            org.springframework.context.annotation.PropertySource.class)) {
        if (this.environment instanceof ConfigurableEnvironment) {
            processPropertySource(propertySource);
        }
        else {
            logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
                    "]. Reason: Environment must implement ConfigurableEnvironment");
        }
    }
    // 处理@ComponentScan注解 Process any @ComponentScan annotations
    Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
            sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
    if (!componentScans.isEmpty() &&
            !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
        // @Conditional注解优先判断
        for (AnnotationAttributes componentScan : componentScans) {
            // 根据@ComponentScan中的参数进行扫描,实际上是委托给ClassPathBeanDefinitionScanner处理
            // The config class is annotated with @ComponentScan -> perform the scan immediately
            Set<BeanDefinitionHolder> scannedBeanDefinitions =
                    this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
            // Check the set of scanned definitions for any further config classes and parse recursively if needed
            for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
                BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
                if (bdCand == null) {
                    bdCand = holder.getBeanDefinition();
                }
                // 如果新解析到的BeanDefinition使用了@Configuration,直接递归解析
                if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
                    parse(bdCand.getBeanClassName(), holder.getBeanName());
                }
            }
        }
    }
    // 处理@Import注解 Process any @Import annotations
    processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
    // 处理@ImportResource注解 Process any @ImportResource annotations
    AnnotationAttributes importResource =
            AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
    if (importResource != null) {
        String[] resources = importResource.getStringArray("locations");
        Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
        for (String resource : resources) {
            String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
            // 将配置的资源存起来,后面会统一处理
            configClass.addImportedResource(resolvedResource, readerClass);
        }
    }
    // 处理@Bean注解 Process individual @Bean methods
    Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
    for (MethodMetadata methodMetadata : beanMethods) {
        // 解析添加了@Bean注解的方法,并存起来,后面统一处理
        configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
    }
    // 递归查询并处理接口中的@Bean注解 Process default methods on interfaces
    processInterfaces(configClass, sourceClass);
    // 判断是否有父类,如果有父类,则返回,外层会递归调用;如果没有则返回null,结束解析 Process superclass, if any
    if (sourceClass.getMetadata().hasSuperClass()) {
        String superclass = sourceClass.getMetadata().getSuperClassName();
        if (superclass != null && !superclass.startsWith("java") &&
                !this.knownSuperclasses.containsKey(superclass)) {
            this.knownSuperclasses.put(superclass, configClass);
            // Superclass found, return its annotation metadata and recurse
            return sourceClass.getSuperClass();
        }
    }
    // No superclass -> processing is complete
    return null;
}

@Component里的 processMemberClasses方法用来处理当前类的内部类,获取当前类的内部类,并循环递归调用processConfigurationClass方法。

@PropertySource用来实现将指定的配置文件加载到当前Spring环境中。

@ComponentScan的作用是自动扫描指定包中的所有类,并根据其是否有特定注解(例如@Service@Component等)将其转化为BeanDefinition加载当上下文中。ComponentScan注解处理方式是将注解中配置的包路径依次委托给ClassPathBeanDefinitionScanner 中进行处理, 如果解析到了新的BeanDefinition且使用了@Configuration注解,直接调用parse方法进行递归解析。

@ImportResource可以将xml类型的配置导入并解析到当前项目中,但是在此处并没有真正进行解析,也将其暂存起来,在后面会统一处理。

@Bean是通过注解方式进行Bean定义最常用的方式,在此处扫描所有加了@Bean注解的方法并将其暂存,后面统一处理。

@Import可以将其他类引入当前上下文中,在该方法中,先通过getImports方法解析需要导入的类型,再调用processImports方法处理这些类型,我们来看看:

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
        Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
        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) {
                // 如果导入的是ImportSelector类型,则将其实例化,并调用其selectImports获取到真实需要导入的类名
                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的子类,就将其放入deferredImportSelectors中,延迟加载,后面会处理
                    if (selector instanceof DeferredImportSelector) {
                        this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
                    }
                    else {
                        // 非DeferredImportSelector子类,直接调用selectImports进行处理
                        String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                        Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
                        processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
                    }
                }
                // 如果导入的类型是ImportBeanDefinitionRegistrar的子类,实例化并调用其Aware接口,将这些实例存起来,后面会统一处理
                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 =
                            ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
                                    this.environment, this.resourceLoader, this.registry);
                    configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
                }
                // 即不是ImportSelector也不是ImportBeanDefinitionRegistrar的情况,当做有@Configuration注解的类处理,递归解析
                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), exclusionFilter);
                }
            }
        }
        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();
        }
    }
}

processImports方法中,循环处理每个需要导入的类型,根据其类型分为三种处理方式:如果导入的类实现了ImportSelector接口:ImportSelector是一个动态导入接口,可以实现其selectImports方法,在该方法中根据条件返回最终需要导入的类。而在当前方法的实现则是会实例化这个ImportSelector子类,调用其selectImports方法获取需要导入的类型,并递归调用processImports方法。不过此处有个例外,如果导入类实现了DeferredImportSelector接口,则不会在此处直接调用其selectImports方法,而会延迟调用,在此处只是进行记录,具体调用时机就是在解析方法的最后一步,当前边的都扫描完了,就到它了。

AutoConfigurationImportSelector 就是实现的 DeferredImportSelector ,那么到这里解析执行的时机我们就找到了,就是在这个里了,我们进去看看:

4.3.3  process 方法

对于通过@Import导入的DeferredImportSelector子类延迟处理,在该方法中进行加载,处理逻辑基本与处理ImportSelector类似。

public void process() {
    // 延迟的 import
    List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
    this.deferredImportSelectors = null;
    try {
        if (deferredImports != null) {
            DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
            deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
            deferredImports.forEach(handler::register);
            // 执行
            handler.processGroupImports();
        }
    }
    finally {
        this.deferredImportSelectors = new ArrayList<>();
    }
}
public void processGroupImports() {
    for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
        Predicate<String> exclusionFilter = grouping.getCandidateFilter();
        grouping.getImports().forEach(entry -> {
            ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
            try {
                // 最后还是会调到 processImports
                processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
                        Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
                        exclusionFilter, false);
            }
            catch (BeanDefinitionStoreException ex) {
                throw ex;
            }
            catch (Throwable ex) {
                throw new BeanDefinitionStoreException(
                        "Failed to process import candidates for configuration class [" +
                                configurationClass.getMetadata().getClassName() + "]", ex);
            }
        });
    }
}

我们画个图理解一下:

看了这么多就一句话就是找@Import注解是什么时候解析执行的哈。

4.4  加载BeanDefinition

那顺便我们看一下解析完后的加载,ConfigurationClassBeanDefinitionReader#processConfigBeanDefinitions方法中,前文我们已经把ConfigurationClassParser#parse方法分析完了,接下来就是ConfigurationClassBeanDefinitionReader#loadBeanDefinitions方法。这里调用的方法名称为loadBeanDefinitions,直译过来就是加载BeanDefinition,但其实根据上面的阅读可以发现,前面解析时已经加载了很多BeanDefinition了,但是对于有些情况只做了记录,没有真正进行加载,而处理这些工作,正是ConfigurationClassBeanDefinitionReader#loadBeanDefinitions做的事情。

public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
    TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
    for (ConfigurationClass configClass : configurationModel) {
        // 再遍历一次候选类,加载之前未完成的工作
        loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
    }
}
/**
 * Read a particular {@link ConfigurationClass}, registering bean definitions
 * for the class itself and all of its {@link Bean} methods.
 */
private void loadBeanDefinitionsForConfigurationClass(
        ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
    if (trackedConditionEvaluator.shouldSkip(configClass)) {
        String beanName = configClass.getBeanName();
        if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
            this.registry.removeBeanDefinition(beanName);
        }
        this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
        return;
    }
    // 如果当前类是被@Import进来的,那么当前类型需要注册成BeanDefinition
    if (configClass.isImported()) {
        registerBeanDefinitionForImportedConfigurationClass(configClass);
    }
    //依次加载@Bean注解的方法
    for (BeanMethod beanMethod : configClass.getBeanMethods()) {
        loadBeanDefinitionsForBeanMethod(beanMethod);
    }
    // 加载@ImportResource注解对应的资源
    loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
    // 处理通过@Import导入的ImportBeanDefinitionRegistrar类型
    loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
  • 将被@Import引入的类自身注册成BeanDefinition
  • 将被@Bean注解的方法解析成BeanDefinition并注册
  • 加载被@ImportResource依赖的配置文件
  • 被@Import导入的ImportBeanDefinitionRegistrar类在此处处理

其中配置文件的加载,是被委托给对应的BeanDefinitionReader加载的,例如xml文件被委托给XmlBeanDefinitionReader处理,这个过程与传统的Spring项目的启动时加载配置文件的过程是一样的。对于ImportBeanDefinitionRegistrar子类的处理过程是依次调用了其registerBeanDefinitions方法,而其子类可以在这个方法中动态加载BeanDefinition

那么再过头来我们的 AutoConfigurationImportSelector 由于是延迟的,所以被暂时包装成 DeferredImportSelectorHolder,放进了 ConfigurationClassParser 解析器的 DeferredImportSelectorHandler 类型的 deferredImportSelectorHandler 对象的 deferredImportSelectors集合变量中。

5  AutoConfigurationImportSelector 解析过程

那接下来我们就来看看  AutoConfigurationImportSelector的执行,执行入口就是我们上边说的:

// ConfigurationClassParser#DeferredImportSelectorHandler#process
public void process() {
    // 延迟的 ImportSelector 集合
    List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
    // 应该是防止死循环或者引入多次,直接赋值空
    this.deferredImportSelectors = null;
    try {
        if (deferredImports != null) {
            // 不空的话,创建一个 DeferredImportSelectorGroupingHandler 处理器
            DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
            // 排序
            deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
            // 将每个 selector 注册进处理器
            deferredImports.forEach(handler::register);
            // 处理器执行
            handler.processGroupImports();
        }
    }
    finally {
        this.deferredImportSelectors = new ArrayList<>();
    }
}

建议大家看的时候边调试边看,这块这个类内部类啥的好几个类的名字特别像,看的你会很晕,所以一定要调试哈。

可以看到该方法其实就是做了两步:

  • 创建 DeferredImportSelectorGroupingHandler 处理器,并循环注册 Selector
  • 执行处理器的 processGroupImports 方法

那我们先来看下注册。

5.1  ConfigurationClassParser#DeferredImportSelectorGroupingHandler#register 

private class DeferredImportSelectorGroupingHandler {
    private final Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>();
    private final Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>();
    /**
     * 首先要知道 deferredImport 是什么
     * 就是有两个属性的包装
     * configurationClass  就是我们的启动类DemoApplication包装成的 ConfigurationClass对象里边有启动类的注解信息等
     * importSelector  就是我们的 AutoConfigurationImportSelector
     * @param deferredImport
     */
    public void register(DeferredImportSelectorHolder deferredImport) {
        /**
         * deferredImport.getImportSelector() 就是 AutoConfigurationImportSelector
         * AutoConfigurationImportSelector的 getImportGroup()方法 就是SpringBoot提供的
         *
         * group 这里就是 AutoConfigurationImportSelector的内部类 AutoConfigurationGroup.class
         */
        Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
        /**
         * 放进 groupings Map中
         * key就是AutoConfigurationGroup.class value就是 new DeferredImportSelectorGrouping(createGroup(group))
         */
        DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
                (group != null ? group : deferredImport),
                key -> new DeferredImportSelectorGrouping(createGroup(group)));
   // 把当前的延迟类信息放进 grouping中 grouping.add(deferredImport);
/** * 存一份映射信息 * key就是 启动类的注解信息 * value就是包装成的 ConfigurationClass对象 */ this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getConfigurationClass()); } }

可以看到主要的就是:

/**
 * 放进 groupings Map中
 * key就是AutoConfigurationGroup.class value就是 new DeferredImportSelectorGrouping(createGroup(group))
 */
DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
        (group != null ? group : deferredImport),
        key -> new DeferredImportSelectorGrouping(createGroup(group)));

那么就来看看  createGroup(group),记住这里 group 类型是 AutoConfigurationImportSelector的内部类 AutoConfigurationGroup。

private Group createGroup(@Nullable Class<? extends Group> type) {
    // type不为空就是 AutoConfigurationGroup.class
    Class<? extends Group> effectiveType = (type != null ? type : DefaultDeferredImportSelectorGroup.class);
    // 类实例化并且根据Aware赋值 Environment、ResourceLoader、BeanDefinitionRegistry
    return ParserStrategyUtils.instantiateClass(effectiveType, Group.class,
            ConfigurationClassParser.this.environment,
            ConfigurationClassParser.this.resourceLoader,
            ConfigurationClassParser.this.registry);
}

那我们继续跟进去 ParserStrategyUtils.instantiateClass 方法:

static <T> T instantiateClass(Class<?> clazz, Class<T> assignableTo, Environment environment,
        ResourceLoader resourceLoader, BeanDefinitionRegistry registry) {
    Assert.notNull(clazz, "Class must not be null");
    Assert.isAssignable(assignableTo, clazz);
    if (clazz.isInterface()) {
        throw new BeanInstantiationException(clazz, "Specified class is an interface");
    }
    ClassLoader classLoader = (registry instanceof ConfigurableBeanFactory ?
            ((ConfigurableBeanFactory) registry).getBeanClassLoader() : resourceLoader.getClassLoader());
    // 类型实例化也就是创建对象出来
    T instance = (T) createInstance(clazz, environment, resourceLoader, registry, classLoader);
    // 就是根据类是否有某Aware调用相关的Aware方法赋值
    ParserStrategyUtils.invokeAwareMethods(instance, environment, resourceLoader, registry, classLoader);
    return instance;
}

这样通过注册方法,我们的 AutoConfigurationImportSelector 类里的 AutoConfigurationGroup 就被创建出来了哈。

5.2  ConfigurationClassParser#DeferredImportSelectorGroupingHandler#processGroupImports

注册完,那我们看看是如何执行的哈,接着看 processGroupImports 方法:

public void processGroupImports() {
    // this.groupings.values() 是不是就是 AutoConfigurationGroup
    for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
        /**
         * 默认的过滤器
         * private static final Predicate<String> DEFAULT_EXCLUSION_FILTER = className ->
         *             (className.startsWith("java.lang.annotation.") || className.startsWith("org.springframework.stereotype."));
         */
        Predicate<String> exclusionFilter = grouping.getCandidateFilter();
        // 获取import要引入的类集合,然后进行循环遍历
        grouping.getImports().forEach(entry -> {
            ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
            try {
                // 最后还是会调到 processImports
                processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
                        Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
                        exclusionFilter, false);
            }
            catch (BeanDefinitionStoreException ex) {
                throw ex;
            }
            catch (Throwable ex) {
                throw new BeanDefinitionStoreException(
                        "Failed to process import candidates for configuration class [" +
                                configurationClass.getMetadata().getClassName() + "]", ex);
            }
        });
    }
}

可以看到就是通过 getImports 方法获取到要加载的类集合,然后循环调用 processImports,这个我们上边看过了哈就是分三种情况递归的又去执行了。那我们接下来就来看看 getImports 方法:

/**
 * Return the imports defined by the group.
 * @return each import with its associated configuration class
 */
public Iterable<Group.Entry> getImports() {
    /**
     * deferredImports 里有什么 就是我们第一步注册的时候放进去的 配置类信息
     * group 就是我们的 AutoConfigurationImportSelector 类里的 AutoConfigurationGroup
     */
    for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
        // 执行加载类信息
        this.group.process(deferredImport.getConfigurationClass().getMetadata(),
                deferredImport.getImportSelector());
    }
    // 得到一个类信息迭代器
    return this.group.selectImports();
}

在 getImports 方法中就是执行 AutoConfigurationGroup的process方法加载要装配的类信息,然后调用 selectImports 得到一个要装配的类的迭代器,来迭代加载每个类。

最后回到我们的SpringBoot 里的 AutoConfigurationGroup类,我们来看看 process 方法:

/**
 * deferredImportSelector 就是 AutoConfigurationImportSelector 实例
 * annotationMetadata 就是我们启动类上的注解信息
 */
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
    Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
            () -> String.format("Only %s implementations are supported, got %s",
                    AutoConfigurationImportSelector.class.getSimpleName(),
                    deferredImportSelector.getClass().getName()));
    // 收集自动配置的元组
    AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
            .getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
    this.autoConfigurationEntries.add(autoConfigurationEntry);
    for (String importClassName : autoConfigurationEntry.getConfigurations()) {
        this.entries.putIfAbsent(importClassName, annotationMetadata);
    }
}
/**
 * Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata}
 * of the importing {@link Configuration @Configuration} class.
 * @param autoConfigurationMetadata the auto-configuration metadata
 * @param annotationMetadata the annotation metadata of the configuration class
 * @return the auto-configurations that should be imported
 */
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
        AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    }
    // 获取 @EnableAutoConfiguration 注解信息 不存在会直接报错
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    // 通过 SpringFactoriesLoader.loadFactoryNames 加载 META-INF/spring.factories 下的 EnableAutoConfiguration 类信息
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    // 下边就是获取排除的有哪些等信息哈  我就不细看了哈
    configurations = removeDuplicates(configurations);
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(exclusions);
    configurations = filter(configurations, autoConfigurationMetadata);
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return new AutoConfigurationEntry(configurations, exclusions);
}

最后 AutoConfigurationGroup的selectImports 方法返回迭代器就比较简单了,就是基本的集合操作:

@Override
public Iterable<Entry> selectImports() {
    if (this.autoConfigurationEntries.isEmpty()) {
        return Collections.emptyList();
    }
    Set<String> allExclusions = this.autoConfigurationEntries.stream()
            .map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
    Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
            .map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
            .collect(Collectors.toCollection(LinkedHashSet::new));
    processedConfigurations.removeAll(allExclusions);
    return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
            .map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
            .collect(Collectors.toList());
}

到这里执行过程我们就看完了,你应该有点晕的,我们画个图来理解下:

6  小结

好了到这里,自动装配其实就是根据@Import来进行切入的,所以我们接着看了@import的解析时机以及执行时机,有理解不对的地方欢迎指正哈。

posted @ 2023-05-10 15:48  酷酷-  阅读(368)  评论(0编辑  收藏  举报