【栖梧-源码-spring】@Bean从解析到注册到beanDefinitionMap
序幕
本文是我的第一篇博客,自我研读spring源码以来,收获颇丰。自工作以来,疑惑颇多,于是通过网络、同事、领导、技术网友等益友的分享和指导,解决了工作中许多疑难杂症,所以我也非常希望把我工作中、学习中的收获分享给所有需要的人。同时受水平所限,其中必有缺漏不足之处,还望指出。
"道友"解释->志同道合者;
源码阅读技巧
书山有路勤为径,学海无涯需持久!
源码阅读是一件枯燥乏味的事情,这种说法是对没有时间、没有兴趣、没有好奇心的道友来讲的。要想深入理解一个框架的原理,必须了解其实现细节,所以花费大量的时间、精力来吸取里面的营养是必须的。
<源码-spring>系列文章均涉及大量源码,且自建简单的项目是基于springboot 2的版本,如果其他版本的源码可能稍有差异,属正常情况。
如果道友你从来没有看过spring源码,阅读起来将会吃力,请悉知!
本文说明
本文核心讲解的是@Bean注解从解析bean到beanFactory的beanDefiniton中
类 ConfigurationClassParser#doProcessConfigurationClass
package org.springframework.context.annotation;
当spring启动后,走到高级AbstractApplicationContext这个高级容器的最核心的 refresh() 方法后,再进入invokeBeanFactoryPostProcessors(beanFactory)调用后置处理器方法,就会调用静态方法PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()) 开始解析所有的BeanDefinition了,由于本文主要讲解@Bean,所以其他的暂时不详解。
//此类就是解析配置类的入口
@Nullable
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
//先判断是否有@Component注解,
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
//首先递归处理任何成员(嵌套)类
// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass);
}
//处理带有@PropertySource的类,然后将里面变量存到全局的Environment中去
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 注解,注意里面是多个递归解析(如果不清楚是递归的道友进入这个方法可能会晕掉的),这
//里面就会解析所有的@Component注解的,包括@Controller、@Service等这些被@Component注解过的注解,解析后的
//bean全部放到ConfigurationClassParser这个对象的 configurationClasses 这个map里面进行后续处理
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// 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();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
//处理@Import的注解
processImports(configClass, sourceClass, getImports(sourceClass), true);
// Process any @ImportResource annotations
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
String[] resources = importResource.getStringArr