墨风如雪博客

  • 源码小店
  • 导航站
  • 登录
  • java
  • 资源分享
让AI使用变得如此简单
  1. 首页
  2. java
  3. spring
  4. 正文

Spring 中循环依赖问题的产生原因及注意事项

2023年 5月 2日 237点热度 0人点赞 0条评论

1. 循环依赖的产生原因

Java Spring 中循环依赖的原因一般是因为两个或多个 bean 相互依赖,形成了一个循环依赖的环路。通俗地讲,A 依赖 B,B 又依赖 A。

Spring 依赖注入机制的目的是将启动时需要创建的对象(通常称为 bean)及它们之间的依赖关系全部交付给 Spring 容器进行管理和维护,通过配置(如 XML 或注解等方式)告诉 Spring 需要创建哪些对象,每个对象需要依赖哪些其它对象,Spring 运行时会自动去解决对象之间的依赖关系。

循环依赖问题是在 Spring 运行时进行对象依赖注入时引起的,如果在对象依赖注入过程中,出现了 A->B->C->A 这样的环形依赖关系,就会导致循环依赖问题的出现。

具体来讲,Spring 在进行对象注入时有两种策略:构造函数注入和 Setter 方法注入。

构造函数注入:

  1. 容器创建 Bean A 实例。
  2. 创建 Bean A 时,如果发现需要容器创建 Bean B 的实例,则会暂时将 A 对象存储到一个缓存区中,然后记录下所需注入属性的类型和 Bean 名称。
  3. 容器创建 Bean B 实例。
  4. 创建 Bean B 实例时,如果发现需要容器创建 Bean A 的实例,则会暂时将 B 对象存储到一个缓存区中,然后记录下所需注入属性的类型和 Bean 名称。
  5. 容器会尝试按照缓存区中的属性类型和名称获取对应的 Bean 进行注入,这个时候会从缓存区中获取相应的 bean,如果没有则将 A 对象加入到创建 B 对象时记录的 A 需要注入的属性的列表中去,返回到容器去继续初始化其他的 Bean,直到将所有的 Bean 初始化完成,再在之前缓存的 Bean 中进行对应的属性注入。

Setter 方法注入:

  1. 容器创建 A 实例。
  2. 容器注入 A 实例所需的属性,如果发现需要创建 B 实例,则会临时将 A 对象存储起来,并记录需要注入属性的类型和 Bean 名称。
  3. 容器创建 B 实例。
  4. 容器注入 B 实例所需的属性,如果发现需要创建 A 实例,则会临时将 B 对象存储起来,并记录需要注入属性的类型和 Bean 名称。
  5. 容器会尝试按照临时存储的对象类型和名称获取对应的 Bean 进行注入,这个时候会从临时存储的对象中获取相应的 Bean,如果没有则将 A 对象加入到创建 B 对象时记录的 A 需要注入的属性的列表中去(注意和构造函数注入的区别),返回到容器去继续初始化其他 Bean,知道将 BEan 的所有属性注入完成。

需要注意的是,循环依赖并不是所有情况都会出现的,像单例模式下的循环依赖就不会出现,这是因为 Spring 容器缓存的是对象的单例实例,而不是对象本身。

  1. 循环依赖的解决方案

解决循环依赖问题,可以使用构造函数注入或者使用@Lazy注解进行懒加载。

2.1 使用构造函数注入

使用构造函数注入,正是因为构造函数的注入是一次性的,如果需要解决循环依赖,循环依赖的双方需要在构造函数中传递一个代理对象。

假设有A和B依赖于彼此,结构代码如下:

public class A {
    private B b;

    public A(B b) {
        this.b = b;
    }

    // getters and setters
}

public class B {
    private A a;

    public B(A a) {
        this.a = a;
    }

    // getters and setters
}

我们可以使用 Spring 的构造函数注入来解决循环依赖。

<bean id="a" class="com.example.A">
    <constructor-arg ref="b"/>
</bean>

<bean id="b" class="com.example.B">
    <constructor-arg ref="a"/>
</bean>

这样,Spring 在创建 A 的时候会先创建 B,将 B 传入到 A 的构造函数中,再将 A 传入到 B 的构造函数中,这样就成功解决了循环依赖的问题。

2.2 使用 @Lazy 注解进行懒加载

使用 @Lazy 注解进行懒加载是另一种解决循环依赖的方案。在 Spring 5.x 版本中,新增了一个特性,即默认情况下支持循环依赖,它会创建代理对象,并将代理对象暂时注册到 BeanFactory 中。

示例代码如下:

@Lazy
@Component
public class A {
    @Autowired
    private B b;

    // getters and setters
}

@Lazy
@Component
public class B {
    @Autowired
    private A a;

    // getters and setters
}

这样,当创建 A 时,容器中就先存在了一个 A 的代理对象,而真正的 A 对象会在 B 创建完成后再初始化创建,因为此时 B 已经存在了一个代理对象,再把这个代理对象注入到 A 中,就成功解决了循环依赖的问题。

  1. 循环依赖的注意事项

3.1 构造注入不能解决循环依赖的问题

尽管通过构造函数注入很容易解决循环依赖的问题,但是构造注入又陷入另一个问题——它无法解决循环依赖的问题,例如 A 依赖 B,B 依赖 A,在进行构造注入的时候,即便在 A 和 B 中使用构造注入,它也无法在创建两个对象之后自动解决循环依赖。

3.2 按需注入可以解决循环依赖

按需注入指的是 Spring 在需要使用 bean 时才进行注入,而不是在创建时就注入。如果两个 bean 都使用按需注入,那么 Spring 可以在第一次访问 bean 的时候解决循环依赖的问题。

3.3 需要注意的是循环依赖不仅限于同一个 ApplicationContext

如果两个 bean 分别被不同的 ApplicationContext 创建,如果两个 ApplicationContext 之间产生了循环依赖的关系,Spring 也无法直接解决这个问题,需要手动合并这两个 ApplicationContext 或使用其他方法解决。

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: spring 产生原因 循环依赖|问题 注意事项
最后更新:2023年 5月 2日

墨风如雪

一个热爱生活,热爱分享的程序员

打赏 点赞
下一篇 >

文章评论

您需要 登录 之后才可以评论

墨风如雪

一个热爱生活,热爱分享的程序员

最新 热点 随机
最新 热点 随机
8B 模型吊打 671B?数学证明界“卷王”Goedel-Prover-V2 来了! Kiro来了!亚马逊放大招,软件开发要被AI“绑架”了吗? 火速围观!Trae IDE 迎来两大明星模型,Kimi K2 硬核登场,Grok-4 (Beta) 闪耀国际! 告别“打工人”模式,AI“全能选手”RoboNeo 来了! PPT 我自己就能做!智谱新模型“玩转”工作汇报,简直是打工人福音! 你的笔记本也能跑“AI大神”!微软Phi-4-mini-flash-reasoning震撼登场
腾讯云掀桌子了!这个免费CDN,国内秒开还无限流量?别只盯着Suno了,腾讯端出的这盘“王炸”可能要改变游戏规则Kimi变身学术“卷王”,你的论文和报告还好吗?昆仑万维扔出王炸:32B模型干翻671B,代码界迎来全能修理工!8亿参数撬动实时混音!谷歌开源“口袋DJ”,人人都能玩转音乐告别插件时代!OmniGen2:一个模型,通吃所有AIGC神操作
K8s常用命令和使用技巧(超详细) Java学习必备:基础语法知识点梳理 JAVA当中的异常处理机制核心讲解 java Web框架SpringBoot的(超详细总结) 主流AI对话产品侧重点与综合体验指南 告别AI视频“变脸怪”!腾讯混元Hunyuan Custom重磅开源,主体一致性“王炸”来了!
标签聚合
AI 大模型 java deepseek 设计模式 spring 算法 教程

COPYRIGHT © 2023 墨风如雪博客. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

免责声明 - 隐私政策