Spring IOC容器:Bean生命周期与循环依赖解决

Spring IOC容器:Bean生命周期与循环依赖解决

引言

在Java企业级开发领域,Spring框架无疑占据了统治地位,而控制反转作为Spring的核心思想,贯穿了框架的始终。对于大多数开发者而言,日常工作中可能只需要通过@Autowired注解就能完成依赖注入,似乎无需深究其底层实现。然而,当我们在面试中面对“请描述Bean的生命周期”或“Spring是如何解决循环依赖的”这类问题时,或者在生产环境中遇到Bean初始化顺序异常、循环依赖报错等棘手问题时,深入理解IOC容器的内部机制就显得尤为重要。

本文将从源码级别深入剖析Spring Bean的生命周期全过程,并详细解读Spring解决循环依赖的“三级缓存”机制。这不仅是面试的高频考点,更是从“API调用者”进阶为“框架掌控者”的必经之路。

核心概念:Bean的生命周期

Spring Bean的生命周期并不是简单的“创建->使用->销毁”,而是一个复杂且精密的流程。我们可以将其概括为四大阶段:实例化属性赋值初始化销毁

1. 实例化

这是Bean诞生的第一步。Spring会根据BeanDefinition中的信息,通过构造函数反射创建Bean的实例对象。此时对象仅仅是在堆内存中分配了空间,属性均为默认值,是一个“半成品”。

2. 属性赋值

实例化完成后,容器会扫描Bean的属性,将配置的属性值或依赖的其他Bean注入到当前Bean中。这就是我们常说的依赖注入(DI)发生的主要阶段。

3. 初始化

这是生命周期中最复杂、扩展性最强的阶段,包含了多个回调接口的调用:
* Aware接口回调:如果Bean实现了BeanNameAwareBeanFactoryAware等接口,Spring会将Bean名称或容器本身注入给Bean,让Bean感知到容器的存在。
* BeanPostProcessor(前置处理):执行postProcessBeforeInitialization方法。这是Spring留给开发者的扩展点,可以在初始化前对Bean进行修改。
* InitializingBean与init-method:如果Bean实现了InitializingBean接口,会调用afterPropertiesSet()方法;如果定义了init-method,也会在此执行。通常用于执行自定义初始化逻辑。
* BeanPostProcessor(后置处理):执行postProcessAfterInitialization方法。这是最关键的一步,Spring的AOP代理对象就是在此处生成的。

4. 销毁

当容器关闭时,会执行销毁逻辑。包括DisposableBean接口的destroy()方法和自定义的destroy-method

技术原理:循环依赖与三级缓存

循环依赖是指Bean A依赖Bean B,而Bean B又依赖Bean A(或更长的闭环)。在单例模式下,Spring通过三级缓存机制解决了构造器注入以外的循环依赖问题。

三级缓存架构

Spring在DefaultSingletonBeanRegistry中维护了三个Map结构:

  1. 一级缓存 (singletonObjects):存放已经完全初始化好的Bean(成品)。
  2. 二级缓存 (earlySingletonObjects):存放提前暴露的Bean实例,但尚未完成属性填充和初始化(半成品)。
  3. 三级缓存 (singletonFactories):存放ObjectFactory,用于生成早期Bean对象,主要目的是处理AOP代理。

解决流程解析

假设A、B两个Bean相互依赖:

  1. 创建A:A开始实例化,调用构造函数。
  2. 暴露A:A实例化完成后,将A的ObjectFactory放入三级缓存。
  3. 注入B:A开始填充属性,发现依赖B,尝试获取B。
  4. 创建B:B不存在,开始实例化B。
  5. 注入A:B开始填充属性,发现依赖A,尝试获取A。
  6. 获取A:B在一级缓存找不到A,去二级缓存也找不到,最终在三级缓存中找到A的ObjectFactory。通过工厂获取到A的早期对象(如果A需要代理,这里返回的是代理对象),并将其放入二级缓存,删除三级缓存。
  7. B完成:B持有A的引用,完成初始化,放入一级缓存。
  8. A完成:A继续执行,获取到已完成的B,完成初始化,放入一级缓存。

为什么需要三级缓存?
如果仅仅是解决循环依赖,二级缓存足矣。三级缓存的存在是为了处理AOP代理。如果在创建过程中就需要注入代理对象,三级缓存中的ObjectFactory可以智能地决定是返回原始对象还是代理对象,保证了Bean在整个生命周期中的一致性。

实战代码

下面的代码演示了Bean生命周期的完整流程,并通过日志验证了执行顺序。

1. 定义生命周期Bean

```java
package com.example.springdemo;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

/*
* 演示Spring Bean生命周期的完整流程
* 实现了Spring提供的一系列感知接口和生命周期接口
/
public class LifecycleBean implements BeanNameAware, BeanFactoryAware,
ApplicationContextAware, InitializingBean, DisposableBean {

private String property;

// 1. 构造函数:实例化阶段
public LifecycleBean() {
    System.out.println("1. [构造函数] Bean实例化...");
}

// Setter方法:用于演示属性赋值(XML或注解方式注入)
public void setProperty(String property) {
    System.out.println("2. [属性赋值] 注入属性值: " + property);
    this.property = property;
}

// 3. BeanNameAware接口:感知Bean的名称
@Override
public void setBeanName(String name) {
    System.out.println("3. [Aware接口] BeanNameAware -> Bean名称: " + name);
}

// 4. BeanFactoryAware接口:感知BeanFactory
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
    System.out.println("4. [Aware接口] BeanFactoryAware -> 持有BeanFactory引用");
}

// 5. ApplicationContextAware接口:感知ApplicationContext
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    System.out.println("5. [Aware接口]
posted @ 2026-03-01 10:01  寒人病酒  阅读(0)  评论(0)    收藏  举报