文章目录

  • SpringBoot自动装配流程梳理
  • 一、Spring BeanDefinition扫描
  • 1、关键类说明
  • 1.1、`ConfigurationClassPostProcessor`
  • 1.2、`ConfigurationClassParser`
  • 1.3、`ClassPathBeanDefinitionScanner`
  • 1.4、`ConfigurationClassBeanDefinitionReader`
  • 2、流程概要
  • 3、拿到符合条件的bean定义
  • 4、循环的解析封装bean定义
  • 4.1、递归的当前类以及父类
  • 4.2、当前配置类处理
  • 4.2.1、递归解析内部类
  • 4.2.2、解析@PropertySource
  • 4.2.3、解析@ComponentScan、ComponentScans
  • 4.2.4、处理@Import注解
  • 4.2.4.1、处理`ImportSelector`
  • 4.2.4.2、处理`ImportBeanDefinitionRegistrar`
  • 4.2.4.3、处理其他类型
  • 4.2.5、处理@ImportResource注解
  • 4.2.6、处理@Bean注解的方法
  • 4.2.7、解析出父类进行返回
  • 5、根据封装的配置类注册BeanDefinition
  • 5.1、注册BeanDefinition
  • 5.2、导入类的逻辑判断
  • 二、自动装配的顺序处理
  • 1、根据注解排序逻辑
  • 1.1、递归处理所有装配类(@AutoConfigAfter/@AutoConfigBefore)


SpringBoot自动装配流程梳理

一、Spring BeanDefinition扫描

1、关键类说明

参考博客:

工具名称

功能简介

ConfigurationClassPostProcessor

BeanDefinitionRegistryPostProcessor/BeanFactoryPostProcessor接口的实现类,会在应用程序启动过程中,应用上下文执行BeanFactoryPostProcessor阶段被执行,用于处理所有的配置类(使用了注解@Configuration),并注册其中的bean定义

ConfigurationClassParser

从给定的种子配置类开始分析所有有关的配置类,以一组ConfigurationClass对象的形式给调用者使用, 调用者为ConfigurationClassPostProcessor

ConfigurationClassBeanDefinitionReader

从调用者给定的一组ConfigurationClass对象中获取其中的bean定义并注册到容器, 调用者为ConfigurationClassPostProcessor

ComponentScanAnnotationParser

针对注解@ComponentScan分析工具,扫描指定包获取其中的组件bean定义 调用者是ConfigurationClassParser

ClassPathBeanDefinitionScanner

基于classpathbean定义扫描器,扫描bean定义并注册到容器,带有下面的注解的类会被认为是bean定义:@Component,@Repository,@Service,@Controller,@ManagedBean,@Named 调用者是ComponentScanAnnotationParser

1.1、ConfigurationClassPostProcessor
1.2、ConfigurationClassParser
1.3、ClassPathBeanDefinitionScanner

这里防止重复注册(根据bean名称和类型,类型用来判断兼容性。只要有名字,就不会再注册。而类型用来判断是否兼容。不兼容的报错。)

org.springframework.context.annotation.ClassPathBeanDefinitionScanner#checkCandidate

1.4、ConfigurationClassBeanDefinitionReader

入口,用来通过加载ConfigurationClassParser给出的候选配置类加载bean定义

SpringApplication.run()
=>refreshContext(ConfigurableApplicationContext context)
	=> EmbeddedWebApplicationContext.refresh()
		=> AbstractApplicationContext.refresh()
		=>invokeBeanFactoryPostProcessors()
			=> PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()
				=> ConfigurationClassPostProcessor.processConfigBeanDefinitions()
					=> ConfigurationClassBeanDefinitionReader.loadBeanDefinitions()

2、流程概要

SpringApplicationContext构建时注册了一个ConfigurationClassPostProcessorinvokeBeanFactoryPostProcessors调用时进入ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry

整体流程会拿到当前已有的bean定义,循环解析。解析的过程中嵌套了各个环节的递归,总体原则是标注了@Componet的非自动装配类会被立即注册,如果标注的有@Bean@Import等注解,会被封装在parse后的ConfigurationClass中,然后通过ConfigurationClassBeanDefinitionReader去加载。

final class ConfigurationClass {
    private final AnnotationMetadata metadata;
    private final Resource resource;
    @Nullable
    private String beanName;

    // 存放被哪个类导入的
    private final Set<ConfigurationClass> importedBy = new LinkedHashSet<>(1);

    // 存放内部的@Bean方法
    private final Set<BeanMethod> beanMethods = new LinkedHashSet<>();

    private final Map<String, Class<? extends BeanDefinitionReader>> importedResources =
        new LinkedHashMap<>();

    // 存放@Import标注的ImportBeanDefinitionRegistrar类
    private final Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars =
        new LinkedHashMap<>();

    final Set<String> skippedBeanMethods = new HashSet<>();
}

3、拿到符合条件的bean定义

拿到当前所有的bean定义,筛选出configCandidates,根据order排序

这里不一定就是@Configuration

将标注了@Configuration@Component@ComponentScan@Import@ImportResource、含有@Bean的类都添加到configCandidates作为候选进行处理,并标记FULLLITE(注:调用过ConfigurationClassUtils#checkConfigurationClassCandidate方法后才会被标记)

过滤条件代码依据ConfigurationClassUtils#checkConfigurationClassCandidate

public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) {
 if (metadata.isInterface()) {
     return false;
 }

 for (String indicator : candidateIndicators) {
     if (metadata.isAnnotated(indicator)) {
         return true;
     }
 }

 try {
     return metadata.hasAnnotatedMethods(Bean.class.getName());
 }
 catch (Throwable ex) {

     return false;
 }
}private static final Set<String> candidateIndicators = new HashSet<>(8);

static {
 candidateIndicators.add(Component.class.getName());
 candidateIndicators.add(ComponentScan.class.getName());
 candidateIndicators.add(Import.class.getName());
 candidateIndicators.add(ImportResource.class.getName());
}

4、循环的解析封装bean定义

调用ConfigurationClassParser#parse进入解析。

4.1、递归的当前类以及父类

进入ConfigurationClassParser#processConfigurationClass方法,会根据装配条件进行过滤。

doProcessConfigurationClass的返回值是configClass的父类,只要父类存在,循环进行解析。

SourceClass sourceClass = asSourceClass(configClass);
do {
    sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);

this.configurationClasses.put(configClass, configClass);
}
4.2、当前配置类处理

进入ConfigurationClassParser#doProcessConfigurationClass,开始实际的解析

分析配置类,分析过程中

4.2.1、递归解析内部类

方法入口ConfigurationClassParser#processMemberClasses,一旦内部类符合条件,就递归ConfigurationClassParser#parse流程

4.2.2、解析@PropertySource

将解析到的配置加入到Envirenment中

4.2.3、解析@ComponentScan、ComponentScans

委托给了ComponentScanAnnotationParser进行扫描,扫描会根据@ComponentScan声明的过滤条件进行过滤。

  • 如主配置类中@SpringBootApplication主资源类、自动装配列表排除在外,所以不会在扫描时扫到在自动装配名单里的配置类。

@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication

  • 扫描到的组件在ComponentScanAnnotationParser类的流程中不会去处理内部的@Bean方法,而是当做一个普通的@Componet进行注册。
  • 将所有扫描结果返回给调用者ConfigurationClassParserConfigurationClassParser会针对返回的内容再去进行parse阶段的解析。所以在一个@Component注解的类上标注条件注解、扫描或者import类注解也是有效的,并且可以看到parse解析又是递归的。
4.2.4、处理@Import注解

入口ConfigurationClassParser#processImports,本身也是递归的处理

@Import可以导入Configuration, ImportSelector, ImportResource或者一个普通类作为组件。

本方法将拿到的importCandidates(import中声明的要导入的类)根据类型不同分别做处理,含有:ImportSelectorImportBeanDefinitionRegistrar、其他(即@Configuration或普通组件或普通类)

4.2.4.1、处理ImportSelector
  • 如果ImportSelector属于DeferredImportSelector
  • 加入到deferredImportSelectorHandler持有的deferredImportSelectors中,等待当前主配置类(比如标注了@SpringBootApplication的启动类)解析完毕后最后处理。
  • @EnableAutoConfiguration里导入的AutoConfigurationImportSelector就实现了DeferredImportSelector,所以会看到某主配置类下对应的自动装配动作是排在所有bean解析处理结束后的。
  • 因此,对于条件装配的bean,最好的做法就是收录到自动装配中去,通过@AutoConfigureBeforeAutoConfigureAfter@AutoConfigureOrder去控制自动装配之间的依赖关系,从而让依赖能够充分的装配完毕不至于产生困惑的情景,如@ConditionOnMissBean@ConditionOnBean就取决于bean定义的注册顺序,如果装配顺序无法保障,就可能出现意料之外的装配逻辑。
  • 如果不是DeferredImportSelector,那么就调用ImportSelector#selectImports方法拿到要导入的类全限定名列表,递归当前的处理逻辑(ConfigurationClassParser#processImports
4.2.4.2、处理ImportBeanDefinitionRegistrar

加入到当前解析的配置类的importBeanDefinitionRegistrars里。

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());
}
4.2.4.3、处理其他类型

非前面两种类型的,会走此分支。

// 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));

实际上是进入到解析过程了processConfigurationClass。随后就会根据【配置类注解解析】的流程走。如果只是@Componet组件,分两种情况:

  1. 当能前置步骤里的@ComponentScan扫描到时,实际上已经被注册到registry里了,后续进入到processConfigurationClass环节将会去解析(可以参考前面对@ComponetScan里的处理描述),并把嵌套的解析结果加入到当前组件类对应的ConfigurationClass中,以便后续ConfigurationClassBeanDefinitionReader加载定义。
  2. 如果没有被前置步骤扫描到,那么一定是被某个bean导入的(也不是的话,就不会被spring感知,也就没有对应的解析过程了)。进入到processConfigurationClass环节,处理同1,最终会被收录到ConfigurationClassParser#configurationClasses字段中
4.2.5、处理@ImportResource注解

占位符解析后,被收录到importedResources字段中。

// 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);
    }
}
4.2.6、处理@Bean注解的方法

包含当前类中标注了@Bean的方法和当前类实现的接口里(默认接口?)标注的@Bean的方法,最终添加到ConfigurationClass#beanMethods字段。

// Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
    configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// Register default methods on interfaces implemented by the configuration class.
private void processInterfaces(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
    for (SourceClass ifc : sourceClass.getInterfaces()) {
        Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(ifc);
        for (MethodMetadata methodMetadata : beanMethods) {
            if (!methodMetadata.isAbstract()) {
                // A default method or other concrete method on a Java 8+ interface...
                configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
            }
        }
        processInterfaces(configClass, ifc);
    }
}
/**
 * Retrieve the metadata for all <code>@Bean</code> methods.
 */
private Set<MethodMetadata> retrieveBeanMethodMetadata(SourceClass sourceClass) {
    AnnotationMetadata original = sourceClass.getMetadata();
    Set<MethodMetadata> beanMethods = original.getAnnotatedMethods(Bean.class.getName());
    if (beanMethods.size() > 1 && original instanceof StandardAnnotationMetadata) {
        // Try reading the class file via ASM for deterministic declaration order...
        // Unfortunately, the JVM's standard reflection returns methods in arbitrary
        // order, even between different runs of the same application on the same JVM.
        try {
            AnnotationMetadata asm =
                this.metadataReaderFactory.getMetadataReader(original.getClassName()).getAnnotationMetadata();
            Set<MethodMetadata> asmMethods = asm.getAnnotatedMethods(Bean.class.getName());
            if (asmMethods.size() >= beanMethods.size()) {
                Set<MethodMetadata> selectedMethods = new LinkedHashSet<>(asmMethods.size());
                for (MethodMetadata asmMethod : asmMethods) {
                    for (MethodMetadata beanMethod : beanMethods) {
                        if (beanMethod.getMethodName().equals(asmMethod.getMethodName())) {
                            selectedMethods.add(beanMethod);
                            break;
                        }
                    }
                }
                if (selectedMethods.size() == beanMethods.size()) {
                    // All reflection-detected methods found in ASM method set -> proceed
                    beanMethods = selectedMethods;
                }
            }
        }
        catch (IOException ex) {
            logger.debug("Failed to read class file via ASM for determining @Bean method order", ex);
            // No worries, let's continue with the reflection metadata we started with...
        }
    }
    return beanMethods;
}
4.2.7、解析出父类进行返回

如果这一步已经是顶级类,将返回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;

5、根据封装的配置类注册BeanDefinition

对应此行代码this.reader.loadBeanDefinitions(configClasses);

循环调用loadBeanDefinitionsForConfigurationClass

public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
    TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
    for (ConfigurationClass configClass : configurationModel) {
        loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
    }
}
5.1、注册BeanDefinition

loadBeanDefinitionsForConfigurationClass

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;
    }

    if (configClass.isImported()) {
        registerBeanDefinitionForImportedConfigurationClass(configClass);
    }
    for (BeanMethod beanMethod : configClass.getBeanMethods()) {
        loadBeanDefinitionsForBeanMethod(beanMethod);
    }

    loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
    loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

代码中可以看到,依次解析:

  1. 如果当前类是被导入的,那么就注册当前类。(参考导入类的逻辑判断)
  2. 注册beanMethod
  3. 注册ImportedResources中指出的资源
  4. 注册标注的ImportBeanDefinitionRegistrar
5.2、导入类的逻辑判断

springboot Redistemplate 自动装配 springboot自动装配流程_递归

源码中以下两个构造函数被调用时意味着被导入。

// 此方法被ConfigurationClassParser.SourceClass#asConfigClass调用,而后者在递归解析内部类processMemberClasses和ConfigurationClassParser#processImports时,在普通类分支里被调用。
// 即递归处理内部类输入被导入了,processImports时本来就是被导入的。
public ConfigurationClass(Class<?> clazz, @Nullable ConfigurationClass importedBy) {
    this.metadata = new StandardAnnotationMetadata(clazz, true);
    this.resource = new DescriptiveResource(clazz.getName());
    this.importedBy.add(importedBy);
}
// 此类同上,只是metadata来源不同导致
public ConfigurationClass(MetadataReader metadataReader, @Nullable ConfigurationClass importedBy) {
    this.metadata = metadataReader.getAnnotationMetadata();
    this.resource = metadataReader.getResource();
    this.importedBy.add(importedBy);
}

在以下方法被调用时,意味着是被导入

// 此方法在解析到同个类两次时,如果都是被import导入的,就会进行合并。
public void mergeImportedBy(ConfigurationClass otherConfigClass) {
    this.importedBy.addAll(otherConfigClass.importedBy);
}

二、自动装配的顺序处理

1、根据注解排序逻辑

核心方法AutoConfigurationSorter#sortByAnnotation

循环已经根据名字和指明的order排序的自动状态名单。(@AutoConfigOrder)

private List<String> sortByAnnotation(AutoConfigurationClasses classes, List<String> classNames) {
    List<String> toSort = new ArrayList<>(classNames);
    toSort.addAll(classes.getAllNames());
    Set<String> sorted = new LinkedHashSet<>();
    Set<String> processing = new LinkedHashSet<>();
    while (!toSort.isEmpty()) {
        // 这里是个递归
        doSortByAfterAnnotation(classes, toSort, sorted, processing, null);
    }
    sorted.retainAll(classNames);
    return new ArrayList<>(sorted);
}
1.1、递归处理所有装配类(@AutoConfigAfter/@AutoConfigBefore)

org.springframework.boot.autoconfigure.AutoConfigurationSorter#doSortByAfterAnnotation

  • 此方法先把A中的@AutoConfigAfter指定的加入list,后把其他类中@AutoConfigBefore指明了A的类加入list。
private void doSortByAfterAnnotation(AutoConfigurationClasses classes, List<String> toSort, Set<String> sorted,
                                     Set<String> processing, String current) {
    if (current == null) {
        current = toSort.remove(0);
    }
    processing.add(current);
    for (String after : classes.getClassesRequestedAfter(current)) {
        Assert.state(!processing.contains(after),
                     "AutoConfigure cycle detected between " + current + " and " + after);
        if (!sorted.contains(after) && toSort.contains(after)) {
            doSortByAfterAnnotation(classes, toSort, sorted, processing, after);
        }
    }
    processing.remove(current);
    sorted.add(current);
}
// 此方法先把className中的@AutoConfigAfter指定的加入classesRequestedAfter,后把其他类中@AutoConfigBefore指明了A的类加入 classesRequestedAfter。
public Set<String> getClassesRequestedAfter(String className) {
    Set<String> classesRequestedAfter = new LinkedHashSet<>();
    classesRequestedAfter.addAll(get(className).getAfter());
    this.classes.forEach((name, autoConfigurationClass) -> {
        if (autoConfigurationClass.getBefore().contains(className)) {
            classesRequestedAfter.add(name);
        }
    });
    return classesRequestedAfter;
}