Spring 核心原理(含源码方法)解析:从 BeanDefinition 到 Bean 的完整生命周期 再到 三级缓存 实现的演进

0 阅读11分钟

IoC 的加载

1. 通过 BeanDefinitionReader 读取指定的配置文件或注解生成bean的定义信息(得到 BeanDefinition )。

2. 通过 BeanFactoryPostProcessor后置增强器,进行修改 BeanDefinition(元数据)。

  • 比如能修改 scope 属性(单例 Singleton 或 多例 Prototype )。
  • 还可以解析占位符:比如 @Value("${jdbc.url}") ,但 Spring 此时只知道有个占位符,不知道 ${jdbc.url} 具体是多少, BeanFactoryPostProcessor可以读取 properties 文件,找到对应的值。
  • 总的来说,它是 Spring IoC 容器启动早期的一个“上帝视角”插件,你可以通过修改 BeanDefinition 来改变 Bean 的“命运”(比如改变它的作用域、注入的值等)

3. 得到完整BeanDefinition之后,进行创建对象,而这个就是Bean的生命周期


Bean 的生命周期

Bean 生命周期的大致流程

  1. 实例化 (createBeanInstance)↘
  2. 属性赋值 (populateBean)———> doCreatBean 方法
  3. 初始化 (initializeBean)_____↗
  4. 使用 (??)
  5. 销毁 (??)

——————————————————————————————————————————————————

具体流程

实例化前置

使用方法: postProcessBeforeInstantiation

可以讲 postProcessBeforeInstantiation 方法想象成一个拦截器。首先,这个方法默认返回的是 null , 但是如果 Spring 在非常特殊的场景比如 Prototype 作用域的对象池,或者 AspectJ 的静态编织 ),于是它在这个方法里,利用动态代理技术,生成了一个代理对象, 同时返回这个 Proxy Object 。

注:这个方法在 InstantiationAwareBeanPostProcessor 类中

返回值:
  • null(默认也是null):理解为 “放行” ,表示继续创建,接下来执行doCreateBean方法
  • Object :表示我现在有 Proxy Object 了,要直接丢出去,不进行接下来的方法了,即跳过 doCreatBean 和 populateBean 方法,进行所谓“短路”。

——————————————————————————————————————————————————

实例化对象

使用方法: createBeanInstance

doCreateBean 中的 createBeanInstance 方法,运用反射技术,进行创建实例对象

此时,对象已经通过构造函数 new 出来了(实例化完成),但身上还是“光秃秃”的,属性(依赖)都还是 null

——————————————————————————————————————————————————

依赖注入的“总指挥”:populateBean

核心方法:populateBean

在 Bean 实例化(new 出来)之后,Spring 就进入了依赖注入阶段。这个阶段的核心入口就是 populateBean

你可以把它看作是依赖注入的 “总指挥” 。它的主要任务不是亲自干活,而是协调各方资源,最终把属性值填充到 Bean 里。

它做了三件大事:
  1. 安检(前置检查) :调用 postProcessAfterInstantiation,问问大家“这个对象能注入吗?”(只有返回 true 才继续)。
  2. 收集(清单整理) :调用 postProcessProperties,把 @Autowired@Value 等注解解析出来,凑齐所有的依赖,整理成一份最终的属性清单PropertyValues)。
  3. 赋值(真正干活) :最后,调用 applyPropertyValues,拿着这份清单,通过反射,按照清单内容,把值一个一个真正地塞进 Bean 的字段里。

注意:所谓的“待注入属性”,就是你平时在类里写的 private UserService userService; 这种普通的变量字段。这里就是依赖注入真正发生的地方。

——————————————————————————————————————————————————

populateBean 流程第一步:实例化后置(安检口)

核心方法:postProcessAfterInstantiation

这是 populateBean 执行的第一件事。Spring 在这里给我们留了一个 “后悔药” 或者 “安检口” 。此时,对象已经通过构造函数 new 出来了,但身上还是“光秃秃”的(属性为 null)。

返回值决定命运:
  • false:表示 Bean 实例创建好了,但我不希望 Spring 为它进行任何属性填充。

    • 后果:直接跳过后续所有注入流程。这通常用于极端的性能优化或特殊场景。
  • true(默认):表示 Bean 实例已经创建,让 Spring 继续为它填充属性(依赖注入)。

关于 dependencyCheck 的补充(已过时):
虽然源码里还有一个 dependencyCheck 属性用于判断是否继续,但这属于 Spring 早期(XML 时代)的产物,用于检查属性是否都赋值了。在现代开发(注解 + Java Config)中,这个属性已经过时且极少使用(默认返回为 DEPENDENCY_CHECK_NONE )

——————————————————————————————————————————————————

populateBean流程第二步:属性清单修改(预处理)

核心方法:postProcessProperties

这是 populateBean 执行的第二件事。
注:老版本叫 postProcessPropertyValues,现已废弃,但原理一致。

在属性真正注入给 Bean 之前,Spring 会先整理一份 “属性清单”PropertyValues)。这份清单里可能已经有一些 XML 配置好的值,或者 @Value 注解的值。

它的作用:

在这个阶段,我们可以对这份清单进行 “修改”

  • 拦截:检查清单里的值是否合法。
  • 增强:这是 @Autowired 发挥作用的关键时刻!Spring 会在这里扫描 Bean 里的注解,把需要的依赖(比如 UserService加入到这份清单里

注意:此时属性还没有真正赋值给 Bean 对象,我们操作的只是“清单”,而不是 Bean 本身。

——————————————————————————————————————————————————

populateBean 流程第三步:真正的赋值(执行注入)

核心方法:applyPropertyValues

这是 populateBean 做的最后一件事。

在前面的步骤中,我们要么拿到了 XML 配置的值,要么通过 @Autowired 整理好了依赖清单(PropertyValues)。但请注意,到这一步为止,这些值还只是停留在“清单”上,并没有真正进入你的 Bean 对象里(比如 userService 字段此时可能还是 null)。

它的作用:

applyPropertyValues 方法会遍历那份整理好的“属性清单”,然后利用 Java 反射机制,把清单里的值一个一个真正地进 Bean 对象的对应字段中。

——————————————————————————————————————————————————

给容器属性赋值

使用方法:“一堆 Aware 接口的实现方法”

用来让 Bean 感知环境就是说通过一些很nb的操作, 让 Bean 知道自己“身在何处”以及“周围有什么资源”。
比如实现了以下功能:

  • 获取Bean的名字
  • 拿到容器,然后手动调用 context.getBean("xxx") 来获取需要的服务
  • 等等 ...

——————————————————————————————————————————————————

初始化前置

使用方法 postProcessBeforeInitialization()

即在初始化每一个 Bean 之前就会调用的方法

在这个阶段:

  • 修改 Bean 的属性值(查漏补缺)。
  • 对 Bean 进行包装或增强。

注意:此时传入的 bean 参数是原始对象,还不是代理对象。

——————————————————————————————————————————————————

执行初始化方法

使用方法
  1. @PostConstruct 注解
    • 通常写在依赖注入完成后的任意方法上,用于执行初始化逻辑
    • 特点:优先级最高,代码侵入性最小,最推荐使用。
  2. 实现 InitializingBean 接口
    • 自动调用,允许重写
    • 特点:优先级第二,需要实现接口,耦合了 Spring 代码。
  3. 配置文件( xml 或 JavaConfig )中写 init-method
    • bean 标签的 init-method 属性上 或 @Bean(initMethod="...") 注解属性上
    • 特点:优先级最低,配置灵活,完全解耦。
初始化的顺序:
  1. @PostConstruct
  2. InitializingBean.afterPropertiesSet()
  3. init-method

——————————————————————————————————————————————————

初始化后置

使用方法 postProcessAfterInitialization()

同上
即在初始化每一个 Bean 之后就会调用的方法

注意Spring 默认的 AOP(比如 @Transactional)通常是在这里生成的

此时,我们的 “Bean” 真正成熟了

——————————————————————————————————————————————————

使用 Bean

最熟悉的地方,开心地使用它吧!

——————————————————————————————————————————————————

销毁 Bean

多例:

Spring 不进行销毁,不调用任何方法,通过 JVM 的垃圾回收器( GC )进行销毁

单例:
  1. @PreDestroy 注解
  • 特点:优先级最高,最推荐使用。
  1. 先调用 DisposableBean.destroy(),自动调用,允许重写
  • 特点:优先级第二,需要实现接口,耦合了 Spring 代码。
  1. **再调用 destroyMethod,手动调用
  • 特点:优先级最低,配置灵活,完全解耦。
  • 写法:
    • XML 配置:<bean destroy-method="close" ... />
    • Java 配置:@Bean(destroyMethod="close")
销毁的顺序
  1. @PreDestroy
  2. DisposableBean.destroy()
  3. destroy-method

——————————————————————————————————————————————————

表格:Spring Bean 的 生命周期

阶段关键方法/接口核心作用
0. 容器启动/蓝图修改BeanFactoryPostProcessor修改蓝图:在对象创建前,修改 BeanDefinition(如解析 ${} 占位符)。
1. 实例化前置postProcessBeforeInstantiationAOP/短路:如果返回代理对象,后续步骤(实例化、注入等)全跳过。
2. 实例化createBeanInstance (反射)生孩子:利用反射 new 出一个空的原始对象。
3. 实例化后置postProcessAfterInstantiation安检:返回 false 则跳过属性注入;返回 true 继续。
4. 属性清单修改postProcessProperties改清单:在注入前,修改或增强即将注入的属性值(PropertyValues)。
5. 属性赋值applyPropertyValues (在 populateBean 内)填属性:执行依赖注入(@Autowired),把值塞进对象字段。
6. 感知环境Aware 接口们认祖归宗:让 Bean 拿到容器引用(BeanName, BeanFactory)。
7. 初始化前置postProcessBeforeInitialization预处理:在初始化方法执行前,对 Bean 进行最后的加工。
8. 初始化@PostConstruct InitializingBean init-method成年礼:执行自定义初始化逻辑(按此顺序执行)。
9. 初始化后置postProcessAfterInitialization包装/代理:AOP 的核心位置,将原始对象包装成代理对象。
10. 使用用地好开心!!干活:业务调用,Bean 处于就绪状态。
11. 销毁@PreDestroy DisposableBean destroy-method入土:释放资源(仅单例,需手动关闭容器)。

三级缓存解决循环依赖的深入原理

接下来我会通过“用于解决循环依赖的三级缓存行为”进行知识巩固和三级缓存理解。

我将用我上期最后的 “三级缓存的行为模拟” 部分进行拆分描述
(需要请查看链接:juejin.cn/post/762175…

三级缓存的名称和简单作用:

  1. 一级缓存 : singletonObjects
    • 存放 :成品 Bean
    • 作用 :完全初始化
      Bean 已经实例化、注入了属性、执行了初始化方法。这是我们要用的最终对象
  2. 二级缓存 : earlySingletonObjects
    • 存放 :半成品
    • 作用 :已实例化,未初始化
      Bean 已经 new 出来了,属性也注入了,但还没执行 @PostConstruct 等方法。
  3. 三级缓存 :singletonFactories
    • 存放 :工厂 (ObjectFactory)
    • 作用 :实例化刚结束
      这里存的是一个“工厂对象”,它的作用是生产早期引用(主要是为了解决 AOP 代理问题)

三级缓存的行为详述:

首先假设 ServiceA 和 ServiceB 互相依赖,且两者都开启了 AOP(例如都加了 @Transactional

1. IoC 容器启动
  • 读取:通过 BeanDefinitionReader 读取配置文件,解析出 A 和 B 的 BeanDefinition(元数据)。
  • 修改:通过 BeanFactoryPostProcessor(容器级后置处理器),对 BeanDefinition 进行修改(例如解析 ${} 占位符)。
  • 结果:此时容器中有了 A 和 B 的完整定义(完整的BeanDifinition),但还没有创建任何对象

此时 A 和 B 的状态:

  • A :完整的BeanDifinition A
  • B :完整的BeanDifinition B
2. A 开始创建(实例化)

Spring 尝试实例化 A 。

  • 实例化前置__检查:调用 postProcessBeforeInstantiation。因为 A 的 AOP 代理是在后面生成的,所以这里返回 null,表示“放行”,不进行短路。若特殊情况(比如外部池等等,则直接短路到初始化后置方法 postProcessAfterInitialization,即 直接跳过实例化、属性注入、初始化前置和初始化)
  • 创建对象__反射实例化:执行 createBeanInstance ,通过反射 new ServiceA()。此时生成了一个原始对象(TargetA),它的属性目前全为 null。

此时 A 和 B 的状态:

  • A :原始对象(TargetA
  • B :完整的BeanDifinition B
3. A 的工厂进入三级缓存

在属性注入( 即执行 populateBean 流程 和 容器属性赋值,前者包含: postProcessInstantiationpostProcessPropertiesapplyPropertyValues ,后者为 “一堆 Aware 接口的实现方法” )之前,Spring 将 A 的工厂( ObjectFactory A )放入三级缓存 ( singletonFactories )。

关键点:这个工厂里包含了一个逻辑:如果 A 需要 AOP,当别人调用 getObject() 时,它会提前创建代理对象(ProxyA)。

此时 A 和 B 的状态:

  • A :原始对象(TargetA) 和 三级缓存( singletonFactories )中的 A 的工厂( ObjectFactory A )
  • B :完整的BeanDifinition B
4. A 尝试注入 B

A 开始属性赋值( populateBean 流程),发现需要 B 。

*注意!:此时 属性赋值populateBean 流程)将暂停,并且 Aware感知方法是在populateBean流程之后进行,所以还没执行Aware

此时 A 和 B 的状态:

  • A :原始对象(TargetA) 和 三级缓存( singletonFactories )中的 A 的工厂( ObjectFactory A )
  • B :完整的BeanDifinition B
5. 寻找 B(失败)

Spring 在一级缓存( singletonObjects )、二级缓存( earlySingletonObjects )中查找 ServiceB,没找到。
于是: 暂停 A 的属性填充populateBean 流程),转而去实例化创建 B 。

此时 A 和 B 的状态:

  • A :原始对象(TargetA)(半填充) 和 三级缓存( singletonFactories )中的 A 的工厂( ObjectFactory A )
  • B :完整的BeanDifinition B
6. B 开始创建(实例化)

Spring 尝试实例化创建 B 。通过 createBeanInstance 反射出 new ServiceB() ,生成原始对象(TargetB)。

此时 A 和 B 的状态:

  • A :原始对象(TargetA)(半填充) 和 三级缓存( singletonFactories )中的 A 的工厂( ObjectFactory A )
  • B :原始对象(TargetB
7. B 的工厂入三级缓存

同样地,Spring 将 B 的工厂( ObjectFactory B )放入三级缓存( singletonFactories )。

此时 A 和 B 的状态:

  • A :原始对象(TargetA) 和 三级缓存( singletonFactories )中的 A 的工厂( ObjectFactory A )
  • B :原始对象(TargetB)和 三级缓存( singletonFactories )中的 B 的工厂( ObjectFactory B )
8. B 尝试注入 A(关键破局点)

B 开始属性赋值( populateBean 流程 ),发现需要 A 。
此时 B 暂停属性注入( populateBean 流程 ),同样Aware还没执行

  • 查一级缓存( singletonObjects )?无。
  • 查二级缓存( earlySingletonObjects )?无。
  • 查三级缓存( singletonFactories )?找到了 A 的工厂!

此时 A 和 B 的状态:

  • A :原始对象(TargetA)(半填充) 和 三级缓存( singletonFactories )中的 A 的工厂( ObjectFactory A )
  • B :原始对象(TargetB)和 三级缓存( singletonFactories )中的 B 的工厂( ObjectFactory B )
9. A 的工厂( ObjectFactory A )生成代理对象 ProxyA

Spring 调用 A 的工厂( ObjectFactory A ) 进行 getObject()

  • AOP 介入:工厂发现 A 需要 AOP,于是提前执行了初始化后置逻辑,生成了 ProxyA(代理对象)。
  • 缓存转移:Spring 将 ProxyA 放入二级缓存 (earlySingletonObjects),并立即从三级缓存( singletonFactories )中移除 A 的工厂( ObjectFactory A )。

此时 A 和 B 的状态:

  • A :存放在二级缓存 (earlySingletonObjects)中的 ProxyA(半填充)
  • B :原始对象(TargetB)(半注入) 和 三级缓存( singletonFactories )中的 B 的工厂( ObjectFactory B )
10. B 注入 ProxyA 并完成
  • B 拿到了 ProxyA ,注入到自己的属性中。
  • B 继续执行后续的 Aware方法 和 初始化方法( @PostConstruct 等 ),最终生成 B 的代理对象 ProxyB
  • ProxyB 被放入 一级缓存 ( singletonObjects ),同时清理 B 在二、三级缓存中的记录。

此时 A 和 B 的状态:

  • A :存放在二级缓存 (earlySingletonObjects)中的 ProxyA(半注入)
  • B :存放在一级缓存 ( singletonObjects )中的 ProxyB
11. A 注入 ProxyB 并完成
  • B 创建完毕,Spring 回到 ServiceA 的创建流程。
  • ProxyA 从一级缓存中拿到了完整的 ProxyB,注入到自己的属性中。
  • ProxyA 继续执行 Aware方法,进行环境感知
  • ProxyA 继续执行初始化方法(@PostConstruct 等)。

最终,ProxyA注意:这里复用的是二级缓存里的那个代理对象)被放入一级缓存 ( singletonObjects ),同时清理 ProxyA 在二级缓存 (earlySingletonObjects) 中的记录。

此时 A 和 B 的状态:

  • A :存放在一级缓存 ( singletonObjects )中的 ProxyA
  • B :存放在一级缓存 ( singletonObjects )中的 ProxyB
12. A 和 B 继续执行 SpringBean 的生命周期,使用 Bean 、销毁 Bean 。
  • 到此结束了这两个互相纠缠的 A 和 B 的一生

本文结束
如果这篇文章帮你理清了思路,我也感到很开心。
这也同时是我的学习笔记,能帮到别人我不胜感谢,若哪里出错希望指出,同样不胜感激!

以上,@Aroaku