首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >揭秘 Spring 三级缓存:从源码到实战,彻底搞懂循环依赖的终极解决方案

揭秘 Spring 三级缓存:从源码到实战,彻底搞懂循环依赖的终极解决方案

作者头像
果酱带你啃java
发布2026-04-14 10:12:27
发布2026-04-14 10:12:27
470
举报

引言:被循环依赖 "坑" 过的请举手

你是否遇到过这样的场景:启动 Spring 应用时突然抛出BeanCurrentlyInCreationException,提示 "Requested bean is currently in creation: Is there an unresolvable circular reference?"?这就是 Spring 中典型的循环依赖问题。当 A 依赖 B,B 又依赖 A 时,就形成了最简单的循环依赖。

如果没有特殊处理,这种相互依赖会导致无穷无尽的初始化过程,最终以失败告终。Spring 通过三级缓存机制优雅地解决了这个问题,但很多开发者只知其然,不知其所以然。本文将从底层原理到实战案例,全方位剖析 Spring 三级缓存的工作机制,让你不仅能解决实际问题,更能理解其设计思想。

一、Spring Bean 的生命周期:理解缓存的前提

在深入三级缓存之前,我们必须先掌握 Spring Bean 的完整生命周期。因为缓存的设计与 Bean 的创建过程紧密相关。

1.1 Bean 的创建流程概览

Spring Bean 的创建过程大致可以分为以下阶段:

代码语言:javascript
复制

关键节点说明:

  • 实例化:创建 Java 对象(内存分配),此时对象处于 "原始状态"
  • 属性填充:注入依赖的其他 Bean
  • 初始化:执行自定义初始化逻辑
  • AOP 增强:生成代理对象(如果需要)

1.2 循环依赖的产生时机

循环依赖可能发生在属性填充阶段。例如:

  • Bean A 实例化后,需要注入 Bean B
  • 此时 Bean B 尚未创建,Spring 会去创建 Bean B
  • Bean B 实例化后,需要注入 Bean A
  • 此时 Bean A 处于 "已实例化但未初始化" 状态

如果没有缓存机制,Spring 会尝试再次创建 Bean A,导致循环调用。

二、三级缓存的核心架构

Spring 通过三级缓存解决循环依赖,这三级缓存分别存储不同状态的 Bean 对象。

2.1 三级缓存的定义

DefaultSingletonBeanRegistry类中,定义了三级缓存的核心数据结构:

代码语言:javascript
复制
/**
 * 一级缓存:存储完全初始化完成的单例Bean
 */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/**
 * 二级缓存:存储早期暴露的单例Bean(已实例化但未完成初始化)
 */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

/**
 * 三级缓存:存储Bean的工厂对象,用于生成早期暴露的Bean
 */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
代码语言:javascript
复制

2.2 三级缓存的分工

代码语言:javascript
复制
  • 一级缓存:存放可用的成熟 Bean,即完全初始化完成的 Bean
  • 二级缓存:存放早期暴露的 Bean 实例,即已实例化但未完成属性填充和初始化的 Bean
  • 三级缓存:存放 Bean 的工厂对象,用于在需要时创建早期暴露的 Bean 实例(可能是原始对象或代理对象)

三、三级缓存解决循环依赖的完整流程

让我们通过 A 依赖 B,B 依赖 A 的经典循环依赖场景,详细分析三级缓存的工作流程。

3.1 流程分解

代码语言:javascript
复制

3.2 关键步骤详解

  1. 创建 Bean A
    • 实例化 A(调用构造方法)
    • 将 A 的工厂对象添加到三级缓存:singletonFactories.put("a", () -> getEarlyBeanReference("a", mbd, a))
    • 准备为 A 填充属性,发现依赖 B
  2. 创建 Bean B
    • 实例化 B(调用构造方法)
    • 将 B 的工厂对象添加到三级缓存:singletonFactories.put("b", () -> getEarlyBeanReference("b", mbd, b))
    • 准备为 B 填充属性,发现依赖 A
  3. 解决依赖
    • 尝试从一级缓存获取 A,失败
    • 尝试从二级缓存获取 A,失败
    • 从三级缓存获取 A 的工厂对象,调用getObject()方法获取 A 的早期引用
    • 将 A 从三级缓存移到二级缓存
    • B 注入 A,完成属性填充和初始化
    • B 从三级缓存移到一级缓存
    • A 注入 B,完成属性填充和初始化
    • A 从二级缓存移到一级缓存

3.3 为什么需要三级缓存?

你可能会问:为什么不直接使用二级缓存?这涉及到 AOP 代理的场景。

当 Bean 需要被 AOP 增强时,Spring 会创建代理对象。如果在循环依赖中直接暴露原始对象,后续的代理对象创建会导致不一致。三级缓存通过工厂对象延迟创建代理对象,确保在需要时才生成正确的代理对象。

代码语言:javascript
复制
/**
 * 从三级缓存获取早期Bean引用的核心方法
 */
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
            // 允许BeanPostProcessor(如AOP)修改早期暴露的Bean
            exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
        }
    }
    return exposedObject;
}
代码语言:javascript
复制

这个方法会应用所有SmartInstantiationAwareBeanPostProcessor,其中AbstractAutoProxyCreator会在这里创建代理对象(如果需要)。

四、实战案例:循环依赖的演示与分析

让我们通过一个实际案例来验证三级缓存的工作机制。

4.1 项目环境配置

pom.xml 关键依赖

代码语言:javascript
复制
<dependencies>
    <!-- Spring Core -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>6.1.2</version>
    </dependency>

    <!-- Lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.30</version>
        <scope>provided</scope>
    </dependency>

    <!-- 日志 -->
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.4.8</version>
    </dependency>
</dependencies>
代码语言:javascript
复制

4.2 循环依赖示例代码

ServiceA.java

代码语言:javascript
复制
package com.ken.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * 演示循环依赖的ServiceA
 *
 * @author ken
 */
@Slf4j
@Service
public class ServiceA {

    private final ServiceB serviceB;

    /**
     * 构造方法注入ServiceB
     *
     * @param serviceB 依赖的ServiceB实例
     */
    @Autowired
    public ServiceA(ServiceB serviceB) {
        log.info("ServiceA构造方法执行");
        this.serviceB = serviceB;
    }

    /**
     * 初始化方法
     */
    public void init() {
        log.info("ServiceA初始化完成");
    }
}
代码语言:javascript
复制

ServiceB.java

代码语言:javascript
复制
package com.ken.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * 演示循环依赖的ServiceB
 *
 * @author ken
 */
@Slf4j
@Service
public class ServiceB {

    private final ServiceA serviceA;

    /**
     * 构造方法注入ServiceA
     *
     * @param serviceA 依赖的ServiceA实例
     */
    @Autowired
    public ServiceB(ServiceA serviceA) {
        log.info("ServiceB构造方法执行");
        this.serviceA = serviceA;
    }

    /**
     * 初始化方法
     */
    public void init() {
        log.info("ServiceB初始化完成");
    }
}
代码语言:javascript
复制

AppConfig.java

代码语言:javascript
复制
package com.ken.config;

import com.ken.service.ServiceA;
import com.ken.service.ServiceB;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 应用配置类
 *
 * @author ken
 */
@Configuration
public class AppConfig {

    /**
     * 定义ServiceA的Bean,指定初始化方法
     *
     * @return ServiceA实例
     */
    @Bean(initMethod = "init")
    public ServiceA serviceA() {
        return new ServiceA(serviceB());
    }

    /**
     * 定义ServiceB的Bean,指定初始化方法
     *
     * @return ServiceB实例
     */
    @Bean(initMethod = "init")
    public ServiceB serviceB() {
        return new ServiceB(serviceA());
    }
}
代码语言:javascript
复制

Main.java

代码语言:javascript
复制
package com.ken;

import com.ken.config.AppConfig;
import com.ken.service.ServiceA;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * 主程序入口
 *
 * @author ken
 */
public class Main {
    public static void main(String[] args) {
        // 启动Spring容器
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        // 获取ServiceA实例
        ServiceA serviceA = context.getBean(ServiceA.class);
        System.out.println("成功获取ServiceA实例:" + serviceA);
    }
}
代码语言:javascript
复制

4.3 运行结果分析

执行 Main 类,控制台输出如下:

代码语言:javascript
复制
ServiceA构造方法执行
ServiceB构造方法执行
ServiceB初始化完成
ServiceA初始化完成
成功获取ServiceA实例:com.ken.service.ServiceA@5a07e868
代码语言:javascript
复制

从输出可以看出:

  1. ServiceA 先实例化(构造方法执行)
  2. ServiceA 依赖 ServiceB,所以 ServiceB 开始实例化
  3. ServiceB 依赖 ServiceA,但此时 ServiceA 已经实例化并暴露在缓存中
  4. ServiceB 完成初始化
  5. ServiceA 完成初始化

这证明 Spring 成功解决了循环依赖问题。

4.4 加入 AOP 的场景

让我们修改案例,为 ServiceA 添加 AOP 增强,看看三级缓存如何处理代理对象。

LoggingAspect.java

代码语言:javascript
复制
package com.ken.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

/**
 * 日志切面,为ServiceA添加增强
 *
 * @author ken
 */
@Slf4j
@Aspect
@Component
public class LoggingAspect {

    /**
     * 在ServiceA的所有方法执行前打印日志
     *
     * @param joinPoint 连接点
     */
    @Before("execution(* com.ken.service.ServiceA.*(..))")
    public void logBefore(JoinPoint joinPoint) {
        log.info("执行ServiceA的方法:{}", joinPoint.getSignature().getName());
    }
}
代码语言:javascript
复制

修改 AppConfig.java,添加@EnableAspectJAutoProxy注解启用 AOP:

代码语言:javascript
复制
package com.ken.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
// 其他import省略

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
    // 内容不变
}
代码语言:javascript
复制

运行结果

代码语言:javascript
复制
ServiceA构造方法执行
ServiceB构造方法执行
ServiceB初始化完成
ServiceA初始化完成
成功获取ServiceA实例:com.ken.service.ServiceA$$SpringCGLIB$$0@6d311334
代码语言:javascript
复制

注意最后一行,ServiceA 的实例已经是 CGLIB 代理对象(`

SpringCGLIB0`标识)。这说明三级缓存成功处理了需要 AOP 增强的循环依赖场景。

五、源码深度剖析:三级缓存的实现细节

要真正理解三级缓存,必须深入 Spring 源码,看看关键流程是如何实现的。

5.1 获取 Bean 的核心方法

AbstractBeanFactorydoGetBean方法是获取 Bean 的入口:

代码语言:javascript
复制
protected <T> T doGetBean(
        String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
        throws BeansException {

    String beanName = transformedBeanName(name);
    Object bean;

    // 检查单例缓存中是否已有该Bean
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
        // 从缓存中获取Bean实例
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    } else {
        // 处理原型Bean的循环依赖(Spring不支持)
        if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }

        // 尝试从父容器获取
        BeanFactory parentBeanFactory = getParentBeanFactory();
        if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
            // 省略代码...
        }

        if (!typeCheckOnly) {
            markBeanAsCreated(beanName);
        }

        try {
            // 省略代码...

            // 创建单例Bean
            if (mbd.isSingleton()) {
                sharedInstance = getSingleton(beanName, () -> {
                    try {
                        return createBean(beanName, mbd, args);
                    } catch (BeansException ex) {
                        destroySingleton(beanName);
                        throw ex;
                    }
                });
                bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            }
            // 处理原型Bean等其他作用域...
        } catch (BeansException ex) {
            cleanupAfterBeanCreationFailure(beanName);
            throw ex;
        }
    }

    // 省略代码...
    return adaptBeanInstance(name, bean, requiredType);
}
代码语言:javascript
复制

5.2 从缓存获取单例 Bean

DefaultSingletonBeanRegistrygetSingleton方法实现了从三级缓存获取 Bean 的逻辑:

代码语言:javascript
复制
public Object getSingleton(String beanName) {
    return getSingleton(beanName, true);
}

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 1. 先从一级缓存获取
    Object singletonObject = this.singletonObjects.get(beanName);
    // 如果一级缓存没有,且Bean正在创建中
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            // 2. 从二级缓存获取
            singletonObject = this.earlySingletonObjects.get(beanName);
            // 如果二级缓存没有,且允许早期引用
            if (singletonObject == null && allowEarlyReference) {
                // 3. 从三级缓存获取工厂对象
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // 通过工厂获取Bean实例
                    singletonObject = singletonFactory.getObject();
                    // 移到二级缓存,三级缓存移除
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}
代码语言:javascript
复制

这个方法清晰地体现了三级缓存的查询顺序:一级 → 二级 → 三级。

5.3 创建单例 Bean 的过程

DefaultSingletonBeanRegistry的另一个getSingleton重载方法负责创建单例 Bean:

代码语言:javascript
复制
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(beanName, "Bean name must not be null");
    synchronized (this.singletonObjects) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {
            // 标记Bean开始创建
            beforeSingletonCreation(beanName);
            boolean newSingleton = false;
            try {
                // 调用工厂方法创建Bean(会调用createBean)
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
            } catch (IllegalStateException ex) {
                // 省略异常处理...
            } finally {
                // 取消创建标记
                afterSingletonCreation(beanName);
            }
            if (newSingleton) {
                // 添加到一级缓存
                addSingleton(beanName, singletonObject);
            }
        }
        return singletonObject;
    }
}
代码语言:javascript
复制

5.4 暴露早期 Bean 引用

AbstractAutowireCapableBeanFactorydoCreateBean方法中,会在 Bean 实例化后暴露早期引用:

代码语言:javascript
复制
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
        throws BeanCreationException {

    // 实例化Bean
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
        instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    if (instanceWrapper == null) {
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    Object bean = instanceWrapper.getWrappedInstance();
    Class<?> beanType = instanceWrapper.getWrappedClass();
    if (beanType != NullBean.class) {
        mbd.resolvedTargetType = beanType;
    }

    // 省略代码...

    // 处理单例Bean的循环依赖
    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
            isSingletonCurrentlyInCreation(beanName));
    if (earlySingletonExposure) {
        if (logger.isTraceEnabled()) {
            logger.trace("Eagerly caching bean '" + beanName +
                    "' to allow for resolving potential circular references");
        }
        // 添加到三级缓存
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }

    // 初始化Bean实例
    Object exposedObject = bean;
    try {
        // 属性填充(依赖注入)
        populateBean(beanName, mbd, instanceWrapper);
        // 初始化Bean
        exposedObject = initializeBean(beanName, exposedObject, mbd);
    } catch (Throwable ex) {
        // 省略异常处理...
    }

    // 省略代码...

    return exposedObject;
}
代码语言:javascript
复制

5.5 添加到一级缓存

当 Bean 完全初始化后,会被添加到一级缓存:

代码语言:javascript
复制
protected void addSingleton(String beanName, Object singletonObject) {
    synchronized (this.singletonObjects) {
        // 添加到一级缓存
        this.singletonObjects.put(beanName, singletonObject);
        // 从三级缓存移除
        this.singletonFactories.remove(beanName);
        // 从二级缓存移除
        this.earlySingletonObjects.remove(beanName);
        // 记录已注册的单例
        this.registeredSingletons.add(beanName);
    }
}
代码语言:javascript
复制

六、三级缓存无法解决的场景

虽然三级缓存很强大,但并非所有循环依赖场景都能解决。

6.1 构造方法循环依赖

如果循环依赖发生在构造方法层面,Spring 无法解决:

代码语言:javascript
复制
@Service
public class ServiceA {
    private final ServiceB serviceB;

    // 构造方法依赖ServiceB
    public ServiceA(ServiceB serviceB) {
        this.serviceB = serviceB;
    }
}

@Service
public class ServiceB {
    private final ServiceA serviceA;

    // 构造方法依赖ServiceA
    public ServiceB(ServiceA serviceA) {
        this.serviceA = serviceA;
    }
}
代码语言:javascript
复制

这种情况下,启动会抛出BeanCurrentlyInCreationException。因为构造方法调用是实例化的前提,而实例化完成后才会暴露到三级缓存。

解决方案

  • 使用字段注入或 setter 注入代替构造方法注入
  • 使用@Lazy注解延迟初始化其中一个依赖
代码语言:javascript
复制
@Service
public class ServiceA {
    private final ServiceB serviceB;

    // 使用@Lazy延迟初始化ServiceB
    public ServiceA(@Lazy ServiceB serviceB) {
        this.serviceB = serviceB;
    }
}
代码语言:javascript
复制

6.2 原型 Bean 的循环依赖

Spring 不支持原型 Bean(prototype)的循环依赖。因为原型 Bean 每次获取都会创建新实例,无法缓存。

代码语言:javascript
复制
@Service
@Scope("prototype")
public class ServiceA {
    @Autowired
    private ServiceB serviceB;
}

@Service
@Scope("prototype")
public class ServiceB {
    @Autowired
    private ServiceA serviceA;
}
代码语言:javascript
复制

这种情况下,Spring 会抛出BeanCurrentlyInCreationException

解决方案

  • 避免原型 Bean 之间的循环依赖
  • 将其中一个 Bean 改为单例

七、常见问题与最佳实践

7.1 如何检测应用中的循环依赖?

Spring 提供了日志支持,通过设置org.springframework.beans.factory的日志级别为 DEBUG,可以看到 Bean 的创建和依赖注入过程。

也可以使用 Spring Boot Actuator 的beans端点,查看 Bean 之间的依赖关系:

代码语言:javascript
复制
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
    <version>3.2.0</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>3.2.0</version>
</dependency>
代码语言:javascript
复制

访问http://localhost:8080/actuator/beans即可查看详细的 Bean 依赖信息。

7.2 循环依赖是设计问题吗?

循环依赖往往暗示着设计上的问题,可能是职责划分不清导致的。例如,两个紧密耦合的类可能应该合并为一个类,或者通过引入第三个类来解耦。

最佳实践

  • 尽量避免循环依赖,通过合理的设计减少耦合
  • 优先使用构造方法注入,它能更早地发现循环依赖问题
  • 必要时才使用字段注入或 setter 注入解决循环依赖

7.3 三级缓存与 Bean 的作用域

三级缓存仅适用于单例(singleton)Bean。其他作用域的 Bean 处理方式不同:

  • 原型(prototype):不缓存,每次请求创建新实例
  • 会话(session)/ 请求(request):在相应的作用域内缓存

八、总结与升华

Spring 的三级缓存机制是解决循环依赖问题的优雅方案,它的设计体现了以下思想:

  1. 分层缓存:通过三级缓存的分层设计,区分了 Bean 的不同状态(成熟、早期、工厂),使复杂的依赖关系处理变得清晰。
  2. 延迟初始化:三级缓存的工厂对象实现了延迟创建代理对象,避免了不必要的代理创建,提高了性能。
  3. 开闭原则:通过BeanPostProcessor等扩展点,使缓存机制具有良好的扩展性,能够处理 AOP 等复杂场景。

理解三级缓存不仅能帮助我们解决实际开发中的循环依赖问题,更能让我们学习到 Spring 框架的设计哲学。在日常开发中,我们应该:

  • 合理设计依赖关系,减少不必要的循环依赖
  • 熟悉 Spring 的 Bean 生命周期,理解各种注入方式的差异
  • 善用 Spring 提供的工具和日志,快速定位和解决问题

Spring 的三级缓存是其内部机制的精华之一,深入理解它,能让我们在使用 Spring 框架时更加得心应手,写出更高质量的代码。

附录:关键类与方法速查表

类名

核心方法

作用

DefaultSingletonBeanRegistry

getSingleton(String beanName)

从三级缓存获取单例 Bean

DefaultSingletonBeanRegistry

addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory)

添加到三级缓存

DefaultSingletonBeanRegistry

addSingleton(String beanName, Object singletonObject)

添加到一级缓存

AbstractAutowireCapableBeanFactory

doCreateBean

创建 Bean 并暴露早期引用

AbstractAutowireCapableBeanFactory

getEarlyBeanReference

获取早期 Bean 引用(可能创建代理)

AbstractBeanFactory

doGetBean

获取 Bean 的入口方法

通过这个速查表,你可以快速定位到三级缓存相关的核心代码,方便深入学习和查阅。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-10-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 果酱带你啃java 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 引言:被循环依赖 "坑" 过的请举手
  • 一、Spring Bean 的生命周期:理解缓存的前提
    • 1.1 Bean 的创建流程概览
    • 1.2 循环依赖的产生时机
  • 二、三级缓存的核心架构
    • 2.1 三级缓存的定义
    • 2.2 三级缓存的分工
  • 三、三级缓存解决循环依赖的完整流程
    • 3.1 流程分解
    • 3.2 关键步骤详解
    • 3.3 为什么需要三级缓存?
  • 四、实战案例:循环依赖的演示与分析
    • 4.1 项目环境配置
    • 4.2 循环依赖示例代码
    • 4.3 运行结果分析
    • 4.4 加入 AOP 的场景
  • 五、源码深度剖析:三级缓存的实现细节
    • 5.1 获取 Bean 的核心方法
    • 5.2 从缓存获取单例 Bean
    • 5.3 创建单例 Bean 的过程
    • 5.4 暴露早期 Bean 引用
    • 5.5 添加到一级缓存
  • 六、三级缓存无法解决的场景
    • 6.1 构造方法循环依赖
    • 6.2 原型 Bean 的循环依赖
  • 七、常见问题与最佳实践
    • 7.1 如何检测应用中的循环依赖?
    • 7.2 循环依赖是设计问题吗?
    • 7.3 三级缓存与 Bean 的作用域
  • 八、总结与升华
  • 附录:关键类与方法速查表
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档