当前位置: 代码网 > it编程>编程语言>Java > Spring创建Bean的关键流程分享

Spring创建Bean的关键流程分享

2026年04月07日 Java 我要评论
一、什么是 bean 生命周期?简单来说,bean 生命周期就是:一个 bean 从创建 → 初始化 → 使用 → 销毁的完整过程换句话说:对象什么时候创建?依赖什么时候

一、什么是 bean 生命周期?

简单来说,bean 生命周期就是:

一个 bean 从创建 → 初始化 → 使用 → 销毁的完整过程

换句话说:

  • 对象什么时候创建?
  • 依赖什么时候注入?
  • 初始化逻辑什么时候执行?
  • 什么时候被销毁?

全部由 spring 控制,而不是开发者手动管理。

二、bean 生命周期整体流程

一个完整的 bean 生命周期可以抽象为:

实例化 → 属性填充 → 初始化 → 使用 → 销毁

但如果展开来看,其实远比这复杂:

1. 实例化 bean

spring 通过反射创建 bean 对象(调用构造方法),此时只是“空对象”,还没有属性值(bean 中的成员变量(字段)所对应的值)。

关键点:只执行构造方法,依赖还没注入

2. 属性填充(依赖注入)

spring 根据配置(xml / 注解如 @autowired)为 bean 注入依赖属性。

关键点:完成对象之间的依赖关系绑定

可能会存在循环依赖处理,下来会有一篇专门讲如何解决

3. aware 接口回调

如果 bean 实现了 beannameawarebeanfactoryawareapplicationcontextaware 等接口,  spring 会回调这些方法,把容器相关信息注入进去。

关键点:让 bean “感知” spring 容器(拿到 beanname、容器对象等)

4. beanpostprocessor 前置处理

所有注册的 beanpostprocessor 会在初始化前执行 postprocessbeforeinitialization() 方法,对 bean 进行增强或修改。

关键点:可以对 bean 做“初始化前加工”(例如修改属性)

5. 初始化(initializingbean / init-method)

执行 bean 的初始化逻辑,比如:

  • afterpropertiesset()(实现 initializingbean)
  • 自定义 init-method

 关键点:开发者定义的“初始化完成后要做的事”

6. beanpostprocessor 后置处理(aop在这里)

执行 postprocessafterinitialization() 方法,这一步常用于生成代理对象(比如 aop)。

关键点:

  • aop 动态代理在这里产生
  • 返回的可能已经不是原始 bean,而是代理对象

7. bean 就绪可用

 bean 完成所有处理,进入容器缓存(单例池),可以被应用程序正常使用。

 关键点:

  • 从这里开始,你 getbean() 拿到的就是最终对象(可能是代理对象)

8. 容器关闭 → 销毁

 当容器关闭时,执行 bean 的销毁方法:

  • destroy()(实现 disposablebean)
  • 自定义 destroy-method

关键点:释放资源(连接、线程、文件等)

三、核心流程详解

1. 实例化 bean

spring 在创建 bean 时,首先会通过反射实例化对象,其核心逻辑位于 createbeaninstance 方法中:

object bean = beanclass.newinstance();

更底层:

constructor<?> constructor = clazz.getdeclaredconstructor();
bean = constructor.newinstance();

此时:

  • 只是一个“空对象”
  • 属性还没有注入
  • 依赖关系还没建立

这一阶段的本质,仅仅是在 jvm 堆中创建了一个对象实例。此时的 bean 还没有任何依赖关系,属性值也全部为默认值,可以理解为一个“空壳对象”。

spring 并不会简单停留在这里,而是在实例化之后立即执行一个非常关键的操作——提前暴露 bean 引用

addsingletonfactory(beanname, () -> getearlybeanreference(beanname, mbd, bean));

这一步的作用是将当前 bean 的“早期引用”放入三级缓存中,从而为后续可能发生的循环依赖提供解决基础

2. 属性填充(依赖注入)

在实例化完成之后,spring 会进入依赖注入阶段:

populatebean(beanname, mbd, instancewrapper);

在这个过程中,spring 会解析 bean 中的依赖(如 @autowired@resource),并通过 resolvedependency 方法递归查找或创建依赖对象,最终通过反射完成注入:

object dependency = resolvedependency(...); 
field.set(bean, dependency);

关键点:

  • 这里会触发 依赖查找
  • 循环依赖问题正是发生在这里!我们可以发现,循环依赖产生的根本原因在于:bean 之间的依赖关系是在初始化完成之前建立的。

3. aware 接口回调(容器能力注入)

当依赖注入完成后,spring 会执行一系列 aware 接口回调,其核心逻辑位于:

invokeawaremethods(beanname, bean);

spring 会判断 bean 是否实现了 beannameawarebeanfactoryawareapplicationcontextaware 等接口,并调用对应方法,例如:

((beannameaware) bean).setbeanname(beanname);

这一阶段的设计目的,是让 bean 可以“感知”容器的存在,从而具备获取上下文信息的能力。

一句话总结:开发者可以主动从 spring 容器里“拿东西”,而不是等容器注入

例如,开发者可以通过 applicationcontext 手动获取其他 bean

applicationcontext context = ...;
userservice userservice = context.getbean("userservice");

从设计角度来看,这是一种典型的“控制反转中的反向注入”。

4. 初始化前处理(beanpostprocessor 前置增强)

在正式初始化之前,spring 会执行 beanpostprocessor 的前置方法,其源码如下:

applybeanpostprocessorsbeforeinitialization(bean, beanname);

底层实现是遍历所有 beanpostprocessor:

for (beanpostprocessor processor : processors) {
    bean = processor.postprocessbeforeinitialization(bean, beanname);
}

beanpostprocessor 的前置处理方法是在初始化方法执行之前调用的,主要用于对 bean 进行统一的增强、修改或预处理,属于 spring 提供的扩展机制;而 @postconstruct 等初始化方法则是开发者定义的,用于完成 bean 自身的初始化逻辑。两者的主要区别在于职责不同,一个偏框架扩展,一个偏业务初始化。

5. 初始化阶段(bean 完整构建)

在前置处理完成后,spring 会进入真正的初始化阶段,其核心方法为:

invokeinitmethods(beanname, bean, mbd);

spring 支持多种初始化方式,例如实现 initializingbean 接口:

afterpropertiesset();

或者通过配置 init-method

<bean init-method="init"/>

beanpostprocessor 前置处理主要用于对 bean 进行统一的增强或修改,属于框架级扩展机制;而初始化阶段(如 @postconstruct 或 afterpropertiesset)则由开发者定义,用于完成 bean 的业务初始化逻辑,使其具备实际可用性。两者的区别在于,前者侧重于“加工 bean”,后者侧重于“使用 bean”。

这一阶段的本质,是对 bean 做最后的“加工处理”,例如参数校验、资源加载、连接初始化等。可以认为,bean 在这里才真正具备“业务可用性”。

6. 初始化后处理(aop 代理生成关键阶段)

初始化完成后,spring 会再次执行 beanpostprocessor,但这一次是后置处理:

applybeanpostprocessorsafterinitialization(bean, beanname);

其核心逻辑通常表现为:

return wrapifnecessary(bean);

这一阶段是 spring aop 实现的关键入口。如果 bean 满足切面条件(如被 @transactional@aspect 等增强),spring 会在这里为其创建代理对象(jdk 动态代理或 cglib)。因此,最终放入容器中的对象,很可能已经不是原始对象,而是一个代理对象。这一点在理解循环依赖时尤为重要,因为“提前暴露”的对象必须与最终对象保持一致,否则会导致代理失效问题。

总结:在 spring 中,如果 bean 被 aop 增强,那么容器中最终存储的并不是原始对象,而是其代理对象,beanname 不发生变化。开发者通过依赖注入或 getbean 获取到的也是该代理对象。原始对象只在 bean 创建过程中短暂存在,不会被放入一级缓存,也不会被直接使用。

7. bean 就绪(进入容器,可被使用)

当上述所有步骤完成后,bean 会被放入单例缓存中(singletonobjects),正式成为容器中的一个可用组件。此时 bean 已经完成了依赖注入、初始化以及可能的 aop 增强,可以被其他 bean 或业务代码正常获取:

context.getbean(a.class);

从生命周期角度来看,这一阶段是 bean 存在时间最长、最稳定的阶段。

8. bean 销毁(生命周期终点)

当 spring 容器关闭时(例如调用 context.close()),会触发 bean 的销毁流程。

其核心逻辑包括:

disposablebean.destroy();

以及:

@predestroy

或:

<bean destroy-method="destroy"/>

这一阶段主要用于释放资源,例如关闭数据库连接、线程池、网络连接等。从设计角度来看,这是对资源管理的一种“兜底机制”,确保系统优雅关闭。

四、总结

从整体来看,spring 中的 bean 生命周期本质上是一个“逐步加工对象”的过程。

从源码执行顺序来看,这一过程可以概括为:实例化 → 提前暴露 → 依赖注入 → 初始化 → aop增强 → 使用 → 销毁。其中一个非常关键的细节在于:依赖注入发生在初始化之前,而 bean 在实例化之后就已经被提前暴露。这意味着,一个 bean 在尚未完全构建完成时,就有可能被其他 bean 所引用。

正是这一设计,引出了一个经典且不可避免的问题:如果两个 bean 之间存在相互依赖关系(如 a 依赖 b,b 又依赖 a),那么在依赖注入阶段就会出现“对象尚未完成初始化却被再次请求”的情况。这种现象就是所谓的循环依赖问题。

那么,spring 是如何在允许这种“半成品对象被引用”的前提下,仍然保证系统正常运行的?又是如何避免无限递归创建的?这正是下一步需要深入分析的核心——基于三级缓存的循环依赖解决机制

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com