spring如何解决循环依赖的问题

spring如何解决循环依赖的问题

@Service
public class A {
    @Autowired
    private B b;
}

@Service
public class B {
    @Autowired
    private A a;
}

A,B 两个类相互依赖,但启动并不报错,Spring 通过 Java 的引用传递,以及提前暴露对象到三级缓存中和延后设置 field 属性解决此问题

Spring 解决循环依赖的过程

  1. 首先查一二级缓存中是否存有 A
  2. 若没有A,spring调用三级缓存中的A对象创建工厂创建A,此时的A并没有完成属性注入(不完整的Bean)并将创建出来的 A放到 Spring 的二级缓存中;
  3. 因为 A的 b 属性需填充,spring到一级二级缓存中查询 B 对象,没查到,触发 B 的创建流程;
  4. spring调用三级缓存中的B对象创建工厂创建B,并将创建出来的 B 对象放到 Spring 的二级缓存中(这个时刻,二级缓存有A,B,一级缓存啥都没有)
  5. B 的 a 属性为空,把二级缓存中的 a填充,此时B完整了!迁入一级缓存;
  6. 填充A 的 b 属性,依次查一级二级缓存,一级缓存中拿到 B,完成属性注入,此时A完整了!迁入一级缓存,循环依赖到此解决。

Spring三级缓存

  • singletonObjects:一级缓存,存放完整的 bean,从该缓存中取出的 bean 可直接用
  • earlySingletonObjects:二级缓存,存放不完整的bean,未完成属性注入和执行 init 方法,用于解决循环依赖
  • singletonFactories:三级缓存,存储能够产生bean的ObjectFactory,不直接存储bean的实例

解决构造器注入引发的循环依赖问题

⚠️下述代码运行报错

@Service
public class A {
    private B b;
    @Autowired
    public A(B b){ this.b = b; }
}

@Service
public class B {
    private A a;
    @Autowired
    public B(A a){ this.a = a; }
}

如何解决?先说解决方案

  1. 通过 @Scope 的 proxyMode 属性来设置类的代理模式

    @Service
    public class A {
        private B b;
        @Autowired
        public A(B b){ this.b = b; }
    }
    // 使用CGLIB和JDK代理均可,不过JDK麻烦点
    @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
    @Service
    public class B {
        private A a;
        @Autowired
        public B(A a){ this.a = a; }
    }
    

    ScopedProxyMode枚举

    • DEFAULT 不使用代理
    • NO 不使用代理
    • INTERFACES 使用JDK动态代理
    • TARGET_CLASS 使用 CGLIB 动态代理
  2. 使用 @Lazy 注解

    @Service
    public class A {
        private B b;
        @Autowired
        public A(B b){ this.b = b; }
    }
    @Service
    public class B {
        private A a;
        @Autowired
        public B(@Lazy A a){ this.a = a; }
    }
    

再阐述原理

这两种方式都是通过动态代理来避免了循环依赖,但与属性注入不同!AB相互依赖,创建A时需要B,而B 是可延迟加载或使用代理模式。那么此时spring创建一个 B 类的代理类 Bproxy,A使用Bproxy完成B属性注入,A完整了,迁入一级缓存,B则直接在一级缓存中找A完成A属性注入。

这里有一个点很重要——A并不是直接依赖B,而是B的代理Bproxy

⚠️ 暂忙!源码解析空时写

posted @   勤匠  阅读(3)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示