SpringApplication 是 springboot 应用的启动类的引导类

run() 是 SpringApplication 中,我们主要使用的方法
PostProcessor 是 springboot 在初始化过程中的重要组件,包含多种类型,如 BeanFactoryPostProcessors、BeanPostProcessors 分别用来完成 bean 工厂和 bean 的初始化动作
一些与 springboot 整合的框架也会频繁使用类似的方式,比如 KafkaListenerAnnotationBeanPostProcessor
AbstractApplicationContext 在 springboot 中扮演重要角色,大部分启动流程实际是通过这个类完成的
ConfigurableListableBeanFactory 是 springboot 实例工厂的接口
其默认实现为 DefaultListableBeanFactory,主要依赖它来实例化对象
DefaultListableBeanFactory 继承自 AbstractBeanFactory
关键方法 doGetBean 和 createBean 在这个抽象类的中
doGetBean 在此类中实现,方法定义了从尝试直接获取对象,到创建对象的流程
doGetBean 在此类中定义,在子类 AbstractAutowireCapableBeanFactory 中实现,用于真正创建实例
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
implements AutowireCapableBeanFactory {
DefaultSingletonBeanRegistry 是 Spring 用于单例模式的 bean 注册器
singletonObjects 、earlySingletonObjects、singletonFactories 是 DefaultSingletonBeanRegistry 中的 3 个容器,如下代码
singletonFactories 工厂缓存,作为三级缓存,存放 beanName<–>bean factoryearlySingletonObjects 早期预览缓存,作为二级缓存,存放 beanName<–>early bean(没有完全创建完)singletonObjects 完全缓存,作为一级缓存,存放 beanName<–>bean(完全创建完)/** Cache of singleton objects: bean name to bean instance. */
//1级, 已经完全实例化好了的 bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of early singleton objects: bean name to bean instance. */
//2级, 没有完全实例化好的 bean
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
/** Cache of singleton factories: bean name to ObjectFactory. */
//3级, bean 的工厂
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);


进入 SpringApplication.run,通过 refreshContext 刷新容器

然后进入 AbstractApplicationContext.refresh

这一步骤中注册 spring bean 的后置处理器
依赖注入,基于其中一个 spring bean 的后置处理器 实现

然后进入 AbstractApplicationContext.finishBeanFactoryInitialization ,完成 spring bean 工厂的初始化

此方法的最后一步,就是初始化所有非懒加载的单例对象

这里的 beanFactory 是 DefaultSingletonBeanRegistry
从下图位置 preInstantiateSingletons 开始,springboot 开始实例化非懒加载的单例 beans
为了调试方便,这里使用 条件断点,条件为 bean 的 name,是我们想关注的那几个(否则会有很多 springboot 自己的对象)

1 处,判断当前这个 beanName 对应的是个单例的对象,不是就 pass
2 处,判断当前这个 beanName 对应的是个 工厂,肯定不是,走 else
3 处,正常单例无误,开始获取(获取不到肯定就得创造)

路过若干个重载的 getBean 最终来到 AbstractBeanFactory.doGetBean
在红框位置,检查当前 Bean 是否存在于缓存中,但此时的 bean 还没有标记为创建中,因此被拒绝,如下图红叉处


回到 AbstractBeanFactory.doGetBean 后检查有没有在当前工厂里已经定义了,这里其实是从父工厂里进行检查
看上面的红框,父工厂都是 null ,跳过了
看下面的红框,如果不是只校验类型不校验引用,会将当前 bean 标记为 创建中

接下来检查 bean 是否有 depend on 的限制,当前案例肯定是没有的,跳过

接下来是三种对象的类型的构建,分别是 单例、原型 和 其他
单例,2 处进入创建实例的逻辑,但需要注意,这个逻辑只是定义在红线的匿名内部类中,实际调用时机是 3 处的方法内

原型,可见多了一个 beforePrototypeCreation 方法 和后面的 afterPrototypeCreation

其他,从 1 处可见,在这种情况下,springboot 是懵逼的,所以一言不合就找机会扔异常
同时,从 2 处可见,在这种情况下,4 处提供的 getObject 方法是按照 原型 的过程创建对象的
可以理解为这种创建过程是对 原型 的微调和别名

可以看到默认的其他类型 socpes 如下图所示,可以理解为什么以 原型为模板,因为不可能只有一个 请求 吧

而 其他 scope 和原型的区别在于黄圈的 3 处,这里对于每一种 scope 定义自己 getObject 的行为
这个行为命名为 get 方法,并在 Scope.get 中 调用绿圈 4 处定义的 getObject 的逻辑(即 2 处的逻辑)
当前场景肯定是执行 单例 分支,即上面 单例截图 3 处
来到 DefaultSingletonBeanRegistry.getSingleton 可见 单例截图 的 2 处其实就是下图 1 处的入参,并在下图 2 处被使用

注意上图的 beforeSingletonCreation 更下面还有一个 afterSingletonCreation 可见 beforeXXCreation、createBean、afterXXCreation 是 springboot 创建实例的套路
同时此方法将正在创建的 bean 标记为 创建中的单例( 放进 singletonsCurrentlyInDestruction ),这里就是上面 getSingleton 没有通过的地方 ,点此跳转,第二次就不会被此条件拒绝

DefaultSingletonBeanRegistry 类上文说过,是实例化单例对象的重要类,里面有三个 map,作为三级缓存
为了方便观察,添加下面的变量

从此位置,即 DefaultSingletonBeanRegistry.getSingleton的 2 处 (点击跳转)

进入上文 单例截图,即 AbstractBeanFactory.doGetBean 的 2 处(点击跳转)
进而进入 AbstractAutowireCapableBeanFactory 接下来是一大串这个类中逻辑
从上文AbstractBeanFactory.doGetBean 会进入此类的 createBean 中,可以发现其中有个 doCreateBean
一般,
doXX方法是XX方法中的干货
因为一些比较周边的逻辑确实算是 XX 逻辑的一部分,但是这些逻辑没有实际解决问题(比如参数校验啥的) ,在这些逻辑中间通常夹着实际解决问题的逻辑(可以简称为干货逻辑)
而这两种逻辑混在一起并且很长时,阅读者很难区分二者,这时,需要将干货逻辑抽取方法,单独存在
但是,这个干货方法的作用实际上就是XX,于是常规套路命名为doXX以作区分(和强调)

首先在 doCreateBean 中,尝试创建对象

获取 bean 的类型,随后通常在下图 2 处尝试是否指定了非空参构造器,如果没有,在 1 处用空参构造器创建对象并返回


然后在 doCreateBean 中,判断当前是否可能存在循环依赖
如果可能(正创建的 bean 是单例,系统参数运行并且已经标记为创建中),则需要将当前 bean 加入工厂缓存,如下图红框

具体逻辑如下

此时可以从以前的表达式看到 AService 已经进入工厂缓存

上图中有个从早期预览缓存删除的逻辑,这是因为在循环依赖场景,标记了 bean 为创建中,但还没有创建出实例(没在完全缓存中),同时系统参数允许,就从这里快速获得,同时将单例放到此缓存中,从注释看,涉及到一个 full singleton lock
接下来 springboot 会尝试给刚刚创建的 AService 的 bean 填充属性(populate 有输入数据的意思)

进入此方法,先尝试获取属性值,现在当然没有

判断是否有创建 bean 过程中需要使用的 beanPostProcessor

从下面的监控里,可以看到有 4 个,其中明显红框处是当前场景真正需要的
@Resource 时,通过 CommonAnnotationBeanPostProcessor 完成注入@Autowired 时,通过 AutowiredAnnotationBeanPostProcessor 完成注入
循环至 CommonAnnotationBeanPostProcessor,进入 postProcessProperties ,如图中步骤
这里发现 bean,即实例化出来的 AService 中有个 BService 的 b 变量
并通过下面的 inject 方法注入

InjectionMetadata.inject 通过其内部类 InjectedElement 完成注入

在这里,获取用于注入的资源,后面如果获取不到就创建,整个过程经过 CommonAnnotationBeanPostProcessor 内部类 ResourceElement.getResourceToInject、CommonAnnotationBeanPostProcessor.getResource、CommonAnnotationBeanPostProcessor.autowireResource、DefaultListableBeanFactory.resolveDependency
最终进入 DefaultListableBeanFactory.doResolveDependency,如下面系列截图




最终到达 DefaultListableBeanFactory.doResolveDependency

doResolveDependency 会在获取注入类型后尝试获取建议值

如下图,这个建议值是在注解上获取的,可能对应 @Resoutce("xxx") 的场景

在下图取得了需要注入的对象的类型

这里有两种可能性:单实现、多实现

一般情况都是单实现,进入 DependencyDescriptor.resolveCandidate 方法

进去后看到一个有点眼熟的东西,继续向下,又一次进入 AbstractBeanFactory.doGetBean
这里已经是创建被依赖的对象的 bean 了


路过这里

又一次通过 DefaultSingletonBeanRegistry.getSingleton 调用 AbstractBeanFactory.doGetBean 的匿名内部类,进入其子类 AbstractAutowireCapableBeanFactory.createBean

随后又进入其中的 doCreateBean,并在红框处创建实例

并判断当前是不是可能出现循环依赖,如果是,继续扔到工厂缓存中,如下图 1 处


然后给当前 bean 填充属性,如上图 2 处,注意 此时的当前 bean 已经是 BService 了

有一次循环至 CommonAnnotationBeanPostProcessor,进入 postProcessProperties,通过 InjectionMetadata.inject 与其内部类 InjectedElement 注入


尝试注入时,继续获取被注入值的资源

和创建 AService 时一样,路过 CommonAnnotationBeanPostProcessor 内部类 ResourceElement.getResourceToInject、CommonAnnotationBeanPostProcessor.getResource、CommonAnnotationBeanPostProcessor.autowireResource、DefaultListableBeanFactory.resolveDependency
最终进入 DefaultListableBeanFactory.doResolveDependency,执行下图代码

第三次通过 getBean,此时又是获取 AService,如下图


但这时,AService 已经被标记为创建中,于是通过下图 √ 处的校验,并在 1、2、3 处依次查找各级缓存
如果是在工厂缓存中找到的 bean ,就对它做一次缓存升级,从 工厂缓存 提升到 早期预览缓存

如此图效果

而回到 AbstractBeanFactory.doGetBean 时,第二次 getBean( AService ) 已经获取到了它的 bean

于是跳过了上次 doGetBean 时走的大段创建过程,到最后一步,从这里返回

回到了 BService 执行了一半的 DefaultListableBeanFactory.doResolveDependency

回到了 CommonAnnotationBeanPostProcessor.autowireResource 并在后面注册依赖


从下面变量看,都注册完成了

继续运行,回到了 InjectionMetadata.InjectedElement.inject 通过红框完成注入(红框底层是 Unsafe 类)


此时,回到 AbstractAutowireCapableBeanFactory.populateBean 应用属性值(但不是注入的意思,之前已经注入成功了)

继续向后,完成 BService 的 createBean,

即完成了 DefaultSingletonBeanRegistry.getSingleton 中 这一步

并在 DefaultSingletonBeanRegistry.getSingleton 最后添加 BService 进完成缓存

Bservice 进入完成缓存,然后删除 工厂缓存(还删了早期预览缓存,并增加了注册单例,但现在不关心)


退出 AbstractBeanFactory.doGetBean 完成 BService 的创建,并回到 DependencyDescriptor.resolveCandidate 方法


继续,就回到了 DependencyDescriptor.resolveCandidate 并取得了填充 AService 属性 b 的 BService bean

然后和注册 AService 一样,注册 BService 为依赖,接着完成注入



最后将 AService 加入完成缓存,完成单例


需要注意理解的是,此时,DefaultSingletonBeanRegistry.preInstantiateSingletons 只是在下图的循环中完成了 AService 的创建 这一轮循环,下面会轮到 BService 的创建 的轮次,但 BService 早就创建完成了

springboot 开始实例化非懒加载单例 bean
springboot 实例化 A getBean 的轮次开始
A doGetBean
A 从缓存中获取失败,因为 A 没有标记为创建中
A 实例化
A 标记为创建中,进入 工厂缓存
A 填充属性,通过 postProcessor
获取 A 需要填充的字段,并尝试解决它
尝试解决它 A 需要填充的字段的类型
尝试解决它 A 需要填充的字段的资源(Resource),DependencyDescriptor.resolveCandidate
触发 B 的 getBean
B doGetBean
B 从缓存中获取失败,因为 B 没有标记为创建中
B 实例化
B 标记为创建中,进入 工厂缓存
B 填充属性,通过 postProcessor
获取 B 需要填充的字段,并尝试解决它
尝试解决它 B 需要填充的字段的类型
尝试解决它 B 需要填充的字段的资源(Resource),DependencyDescriptor.resolveCandidate
触发 A 的 getBean
A doGetBean
A 从缓存中获取成功,返回
B 需要填充的字段的资源(Resource) 获取成功
回到 postProcessor,完成 B 中 a 的注入
B 添加到完成缓存,完成 B 的单例注册
B 创建完成(实例化完成)
==回到 A 触发的 B 的 getBean ==(已完成)
A 需要填充的字段的资源(Resource)已经有了,刚刚 getBean 返回的就是
回到 postProcessor,完成 A 中 b 的注入
A 添加到完成缓存,完成 B 的单例注册
A 创建完成(实例化完成)
springboot 实例化 A getBean 的轮次结束
springboot 实例化 B getBean 的轮次结束
B doGetBean
B 从缓存中获取成功
springboot 实例化 B getBean 的轮次结束