Spring BeanDefinition 与 Bean 生命周期、循环依赖解析
2026/8/27 17:56:12 网站建设 项目流程

一、BeanDefinition:Bean 定义的元数据载体

BeanDefinition 是 Spring 对 Bean 信息的抽象描述,包含了创建 Bean 所需的全部元数据(如类名、作用域、依赖关系、生命周期回调方法等)。Spring 容器通过解析配置(注解、XML、Java 配置)生成 BeanDefinition,再基于 BeanDefinition 实例化和初始化 Bean。其核心底层结构基于AbstractBeanDefinition,关键属性及源码逻辑如下:

1.1 核心属性:scope(作用域)

scope 定义了 Bean 的创建方式和生命周期范围,决定了 Bean 实例的复用规则,默认值为singleton(单例)。

  • singleton(单例,默认): 特性:容器启动时(非懒加载模式下)创建 Bean 实例,容器销毁时销毁实例,整个容器内该 Bean 实例全局唯一;核心源码:AbstractBeanFactory.doGetBean()中判断 scope 为 singleton 时,优先从缓存(一级缓存)获取实例,若缓存不存在则调用createBean()创建,创建后存入缓存复用。

  • prototype(原型):特性:每次通过getBean()获取 Bean 时,都会创建新的实例;Spring 容器仅负责实例化,不管理实例的销毁(销毁由用户自行处理);核心源码:AbstractBeanFactory.doGetBean()中判断 scope 为 prototype 时,直接调用createBean()创建新实例,不存入缓存。

  • request(请求作用域,Web 环境): 特性:绑定 HTTP 请求生命周期,每个请求创建一个新实例,请求结束后实例销毁。

  • session(会话作用域,Web 环境): 特性:绑定 HTTP 会话生命周期,每个会话创建一个新实例,会话失效后实例销毁。

1.2 核心属性:lazyInit(懒加载)

lazyInit 控制单例 Bean 的创建时机,仅对 singleton 作用域有效(prototype 默认为“懒加载”,每次获取都新建)。

  • true(懒加载): 特性:无论容器是否启动,仅在首次通过getBean()获取 Bean 时才创建实例;核心源码:DefaultListableBeanFactory.preInstantiateSingletons()方法中,会跳过标记为懒加载的 Bean,不进行容器启动时的预初始化。

  • false(非懒加载,默认): 特性:容器启动过程中,在refresh()方法的finishBeanFactoryInitialization()阶段完成所有单例 Bean 的预初始化。

1.3 核心属性:autowireMode(自动装配模式)

autowireMode 定义了 Bean 依赖的自动注入规则,决定了 Spring 如何查找并注入依赖的 Bean,默认值为AUTOWIRE_NO(不自动装配)。

  • AUTOWIRE_NO(0,默认):不自动装配,仅支持手动注入(如@Autowired@Resource注解,或 XML 中显式配置)。

  • AUTOWIRE_BY_TYPE(1,按类型装配):根据依赖的 Bean 类型(Class)从容器中查找匹配的 Bean,若存在多个同类型 Bean 则抛出异常。

  • AUTOWIRE_BY_NAME(2,按名称装配):根据依赖的字段名或 setter 方法参数名,匹配容器中同名的 Bean(Bean 名称默认为首字母小写的类名)。

  • AUTOWIRE_CONSTRUCTOR(3,按构造器装配):根据构造方法的参数类型,从容器中查找匹配的 Bean,注入到构造方法中完成实例化。

核心源码:自动装配逻辑在AbstractAutowireCapableBeanFactory.populateBean()方法中执行,该方法会根据 autowireMode 的配置,完成 Bean 的属性注入。

二、Bean 的完整生命周期

Bean 的生命周期是从“基于 BeanDefinition 创建实例”到“容器销毁实例”的全流程,核心分为 5 个阶段:实例化 → 属性复制(依赖注入) → 初始化 → 使用 → 销毁。每个阶段都有对应的扩展点(如接口回调、自定义方法)供开发者介入。

2.1 阶段 1:实例化(Instantiation)

核心目标:创建 Bean 的实例对象(内存中分配空间),此时尚未进行属性注入。

实现方式:

  • 构造函数实例化:Spring 容器通过 BeanDefinition 中的类名,调用默认构造函数或带参构造函数创建实例(最常用方式);

  • 工厂方法实例化:通过配置的工厂类和工厂方法创建实例(如 XML 中配置factory-beanfactory-method)。

2.2 阶段 2:属性复制(Populate Bean)

核心目标:为实例化后的 Bean 注入依赖的属性值(即依赖注入)。

实现方式:

  • setter 方法注入:通过 Bean 的 setter 方法注入属性值(配合 AUTOWIRE_BY_TYPE 或 AUTOWIRE_BY_NAME 自动装配,或手动配置);

  • 字段注入:通过 Java 反射机制直接为 Bean 的字段赋值(如@Autowired字段注入,无需 setter 方法);

  • 构造器注入:在实例化阶段,通过带参构造函数注入依赖(配合 AUTOWIRE_CONSTRUCTOR 自动装配)。

2.3 阶段 3:初始化(Initialization)

核心目标:属性注入完成后,执行额外的初始化操作(如初始化资源、校验属性),确保 Bean 处于可用状态。

扩展方式(执行顺序:接口回调 → 自定义方法):

  • 实现InitializingBean接口:重写afterPropertiesSet()方法,Spring 会在属性注入后自动调用;

  • 自定义初始化方法:通过@PostConstruct注解标记方法,或在 BeanDefinition 中配置init-method(XML 或@Bean(initMethod = "xxx")),Spring 会自动调用该方法。

2.4 阶段 4:使用(In Use)

核心目标:Bean 初始化完成后,可通过 Spring 容器的getBean()方法获取实例,调用其方法执行业务逻辑。此时 Bean 处于完全可用状态。

2.5 阶段 5:销毁(Destruction)

核心目标:容器关闭或手动销毁 Bean 时,执行资源释放等清理操作(如关闭连接池、释放文件资源)。

扩展方式(执行顺序:接口回调 → 自定义方法):

  • 实现DisposableBean接口:重写destroy()方法,Spring 会在容器关闭时自动调用;

  • 自定义销毁方法:通过@PreDestroy注解标记方法,或在 BeanDefinition 中配置destroy-method(XML 或@Bean(destroyMethod = "xxx")),Spring 会自动调用该方法。

三、循环依赖与三级缓存解决方案

循环依赖是指两个或多个 Bean 互相依赖(如 A 依赖 B,B 依赖 A)。Spring 通过“三级缓存”机制解决单例 Bean 的 setter 注入循环依赖,而原型 Bean、构造器注入的循环依赖无法解决。

3.1 三级缓存的核心定义

Spring 容器通过三个缓存(Map 结构)存储不同状态的单例 Bean,协同解决循环依赖:

  • 一级缓存(singletonObjects):存储“完全初始化完成”的单例 Bean 实例(实例化、属性注入、初始化均完成),供全局复用;

  • 二级缓存(earlySingletonObjects):存储“实例化完成,但未初始化”的单例 Bean 实例(仅完成第一步实例化,属性注入和初始化未完成);

  • 三级缓存(singletonFactories):存储“单例 Bean 工厂”(ObjectFactory),用于延迟生成“实例化完成但未初始化”的 Bean 实例(早期引用)。

3.2 三级缓存解决循环依赖的流程(以 A、B 单例 setter 注入为例)

  1. Spring 容器启动时,初始化 A Bean:调用createBean()完成 A 的实例化(未注入属性、未初始化),然后将 A 的单例工厂(singletonFactory)存入三级缓存;

  2. 为 A 注入依赖 B:Spring 从容器中查找 B,发现 B 未创建,开始初始化 B;

  3. 初始化 B Bean:调用createBean()完成 B 的实例化,将 B 的单例工厂存入三级缓存;

  4. 为 B 注入依赖 A:Spring 依次查询一级、二级缓存,均未找到完全初始化的 A;然后查询三级缓存,通过 A 的单例工厂获取 A 的早期引用(实例化完成但未初始化);

  5. 将 A 的早期引用从三级缓存移除,存入二级缓存;然后将 A 的早期引用注入到 B 中;

  6. B 完成属性注入后,执行初始化操作,初始化完成后存入一级缓存(此时 B 中的 A 仍为早期引用,但不影响 B 的初始化);

  7. 回到 A 的属性注入流程:从一级缓存中找到已完全初始化的 B,将 B 注入到 A 中;

  8. A 完成属性注入后,执行初始化操作,初始化完成后存入一级缓存(覆盖之前的早期引用);

  9. 循环依赖解决:A、B 均完成初始化,可正常使用。

3.3 无法解决的循环依赖场景

  • 原型 Bean 循环依赖:原型 Bean 每次获取都新建实例,不存入三级缓存,因此无法通过缓存传递早期引用,会抛出BeanCurrentlyInCreationException异常;

  • 构造器注入循环依赖:构造器注入在实例化阶段就需要依赖对象,而此时依赖对象尚未实例化,无法生成早期引用,因此无法解决,会抛出BeanCurrentlyInCreationException异常。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询