文章目录
SpringBoot自动装配流程梳理
一、Spring BeanDefinition扫描
1、关键类说明
参考博客:https://blog.csdn.net/andy_zhang2007/article/details/85706037
工具名称 | 功能简介 |
---|---|
ConfigurationClassPostProcessor | BeanDefinitionRegistryPostProcessor /BeanFactoryPostProcessor 接口的实现类,会在应用程序启动过程中,应用上下文执行BeanFactoryPostProcessor 阶段被执行,用于处理所有的配置类(使用了注解@Configuration ),并注册其中的bean 定义 |
ConfigurationClassParser | 从给定的种子配置类开始分析所有有关的配置类,以一组ConfigurationClass 对象的形式给调用者使用, 调用者为ConfigurationClassPostProcessor |
ConfigurationClassBeanDefinitionReader | 从调用者给定的一组ConfigurationClass 对象中获取其中的bean 定义并注册到容器, 调用者为ConfigurationClassPostProcessor |
ComponentScanAnnotationParser | 针对注解@ComponentScan 分析工具,扫描指定包获取其中的组件bean 定义 调用者是ConfigurationClassParser |
ClassPathBeanDefinitionScanner | 基于classpath 的bean 定义扫描器,扫描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
构建时注册了一个ConfigurationClassPostProcessor
,invokeBeanFactoryPostProcessors
调用时进入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
作为候选进行处理,并标记FULL
和LITE
(注:调用过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进行注册。 -
将所有扫描结果返回给调用者
ConfigurationClassParser
,ConfigurationClassParser
会针对返回的内容再去进行parse阶段的解析。所以在一个@Component注解的类上标注条件注解、扫描或者import类注解也是有效的,并且可以看到parse解析又是递归的。
4.2.4、处理@Import注解
入口ConfigurationClassParser#processImports
,本身也是递归的处理
@Import可以导入Configuration, ImportSelector, ImportResource
或者一个普通类作为组件。
本方法将拿到的importCandidates
(import中声明的要导入的类)根据类型不同分别做处理,含有:ImportSelector
、ImportBeanDefinitionRegistrar
、其他(即@Configuration或普通组件或普通类)
4.2.4.1、处理ImportSelector
- 如果
ImportSelector
属于DeferredImportSelector
- 加入到
deferredImportSelectorHandler
持有的deferredImportSelectors
中,等待当前主配置类(比如标注了@SpringBootApplication的启动类)解析完毕后最后处理。 @EnableAutoConfiguration
里导入的AutoConfigurationImportSelector
就实现了DeferredImportSelector
,所以会看到某主配置类下对应的自动装配动作是排在所有bean解析处理结束后的。- 因此,对于条件装配的bean,最好的做法就是收录到自动装配中去,通过
@AutoConfigureBefore
、AutoConfigureAfter
和@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组件,分两种情况:
- 当能前置步骤里的@ComponentScan扫描到时,实际上已经被注册到registry里了,后续进入到processConfigurationClass环节将会去解析(可以参考前面对@ComponetScan里的处理描述),并把嵌套的解析结果加入到当前组件类对应的ConfigurationClass中,以便后续
ConfigurationClassBeanDefinitionReader
加载定义。 - 如果没有被前置步骤扫描到,那么一定是被某个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());
}
代码中可以看到,依次解析:
- 如果当前类是被导入的,那么就注册当前类。(参考导入类的逻辑判断)
- 注册beanMethod
- 注册ImportedResources中指出的资源
- 注册标注的ImportBeanDefinitionRegistrar
5.2、导入类的逻辑判断
源码中以下两个构造函数被调用时意味着被导入。
// 此方法被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;
}