什么是循环依赖

什么是循环依赖

现在我们有两个类 ClassA 与 ClassB,但他们互相引用,直接或间接依赖对方,例如例如A类里有B的对象,B类中又有A的对象

public class ClassA {
    private ClassB classB;
    // 构造方法、getter 和 setter 等
}

public class ClassB {
    private ClassA classA;
    // 构造方法、getter 和 setter 等
}

这种依赖不仅限于出现在类上,也可能会出现在包上或者模块上

  • **出现在包层面的危害:

    • 增加了代码的耦合度,降低了代码的可维护性和可读性
    • 在编译时可能会出现编译顺序的问题,难以确定先编译哪个包。
    • 在后续的开发和维护过程中,增加了维护的难度和风险。
  • **出现在模块层面的危害:

    • 会导致模块之间的边界变得模糊,无法清晰地划分模块的职责。在构建和部署项目时,可能会出现循环加载的问题,影响项目的启动效率和运行性能。

Spring中的循环依赖

当两个或多个 Bean 之间存在构造器注入的循环依赖时,Spring 无法解决这种依赖关系。例如,BeanA 的构造器需要 BeanB,而 BeanB 的构造器又需要 BeanA。Spring 容器在创建这些 Bean 的过程中会陷入死循环,无法确定先创建哪个 Bean,从而抛出 BeanCurrentlyInCreationExceptionBeanCurrentlyInCreationException 等异常。

解决方法:

  • Spring 通过单例 Bean 的提前暴露机制来解决基于 Setter 注入的循环依赖问题

  • 创建 BeanA 的壳对象并存入三级缓存 :当创建 BeanA 时,Spring 会先创建一个 BeanA 的壳对象(即一个未完全初始化的实例,只是构造函数执行完成,属性还没有填充),并将其存入三级缓存(SingletonObjectsearlySingletonObjectssingletonFactories 组成的缓存体系)中的 singletonFactories

  • 创建 BeanB 并注入依赖 :接着创建 BeanB,在创建 BeanB 的过程中,发现 BeanB 依赖 BeanA,此时 Spring 会尝试从缓存中获取 BeanA。虽然 BeanA 还没有完全初始化(只是壳对象在 singletonFactories 中),但 Spring 会将其从 singletonFactories 中取出,放入 earlySingletonObjects 中,并将其作为依赖注入到 BeanB 中。

  • 完成 BeanA 的初始化并注入到 BeanBBeanA 的壳对象被注入到 BeanB 后,Spring 会继续完成 BeanA 的初始化(填充属性等操作)。当 BeanA 初始化完成后,会将它放入 SingletonObjects 中。在后续的操作中,如果 BeanB 还需要访问 BeanA,可以直接从 SingletonObjects 中获取已经完全初始化的 BeanA

三级缓存

一级缓存(singletonObjects

一级缓存也被称为单例池,存储的是已经完全初始化好的单例 Bean 实例。当需要获取一个单例 Bean 时,Spring 会优先从这个缓存中查找,其数据结构为 Map<String, Object>,键是 Bean 的名称,值是对应的 Bean 实例。

二级缓存(singletonFactories

当 Bean 实例化完成,但还未完成属性注入和初始化时,会将一个创建该 Bean 代理对象的工厂存入二级缓存。其数据结构为 Map<String, ObjectFactory<?>>,键为 Bean 的名称,值是用于创建 Bean 的工厂对象。如果从一级缓存中未找到所需的 Bean,Spring 会尝试从二级缓存中获取对应的工厂对象,并通过该工厂创建 Bean 的早期暴露实例。

三级缓存(earlySingletonObjects

三级缓存存储的是提前暴露的单例 Bean 实例,这些 Bean 虽然还未完成全部的初始化流程,但已经可以被引用。通过这个缓存,其他 Bean 在依赖该 Bean 时可以获取到一个早期的实例。其数据结构为 Map<String, Object>,键是 Bean 的名称,值是早期暴露的 Bean 实例。如果从二级缓存中获取到工厂对象后,会使用该工厂生成一个早期的 Bean 实例,并将其存入三级缓存中,以便其他 Bean 可以引用。

Posted on:
May 27, 2025
Length:
1 minute read, 172 words
Tags:
面试八股文
See Also:
Java 中的 ConcurrentHashMap 1.7 和 1.8 有什么区别
Java中的HashMap的原理
Prompt、Agent、MCP是什么