java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Spring 依赖注入与循环依赖

Spring 依赖注入与循环依赖总结

作者:xz在努力

这篇文章给大家介绍Spring 依赖注入与循环依赖总结篇,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧

1. Spring 三级缓存解决循环依赖

适用场景:字段注入(@Autowired 直接加在字段上)或 Setter 注入(@Autowired 加在 Setter 方法上)。

解决流程

  1. 一级缓存(singletonObjects):存放完全初始化好的单例 Bean。
  2. 二级缓存(earlySingletonObjects):存放提前暴露的半成品 Bean(已实例化但未初始化)。
  3. 三级缓存(singletonFactories):存放生成 Bean 的 ObjectFactory(用于延迟创建代理对象)。 🔹 关键点:Spring 先创建 Bean 的实例(调用无参构造器),再通过缓存逐步解决依赖,避免死锁。

举例:

个人理解就是,以UserService为例,先创建UserService单例实例放在一级缓存中,如果检测到如@Transition等注解,将单例实例包装成工厂存到三级缓存中,到UserService初始化时发现自注入,分别从一二三级缓存中查找(告诉我查找什么),发现三级缓存中包含UserService 的工厂,然后调用改工厂生成代理对象,并且该代理对象中有对UserService为原始对象的引用,而不是对代理对象的引用,然后生成代理对象存到二级缓存中,并注入到原始对象自注入的UserService中(此时原始对象不是有对代理对象的引用吗?),然后继续完成初始化如@PostConstruct,然后将代理对象放到一级缓存中(此时原始对象还在吗?),最后清楚二级三级缓存中的UserService代理对象和工厂。

你的理解基本正确,但有些细节需要澄清和补充。让我们以 UserService 为例,梳理 Spring 解决循环依赖(自注入)的完整流程,并明确每一步缓存的状态和对象引用关系。

1. 创建UserService原始对象

2. 将原始对象包装成工厂,存入三级缓存

3. 处理UserService的自注入

4. 继续初始化原始对象

5. 将代理对象提升到一级缓存

关键问题解答

理解误区:原始对象和代理对象之间相互引用就是循环依赖因为引用都是唯一的对象。

2. 无法用三级缓存解决的场景

构造器循环依赖(Constructor Circular Dependency):

@Service public class A { 
    private final B b; 
    public A(B b) { 
        this.b = b; 
    } // 构造器依赖 B 
}
@Service public class B { 
    private final A a;
    public B(A a) { 
    this.a = a; 
    } // 构造器依赖 A 
}

多例(Prototype)作用域的循环依赖

3.@Autowired指定构造器注入

@Service 
public class UserService { 
    private final OrderService orderService; 
    @Autowired // 显式指定带参构造器 
    public UserService(OrderService orderService) { 
    this.orderService = orderService; 
    } 
    public UserService() {} // 无参构造器(默认不生效)
}

4.@Value注入配置

@Value("${api.key:default-value}") 
private String apiKey; // 配置缺失时使用 "default-value"
  1. 允许 null
@Value("${api.key:#{null}}") 
private String apiKey; // 配置缺失时注入 nul
  1. 手动检查(复杂场景):
private String apiKey; 
public UserService(Environment env) { 
    this.apiKey = env.getProperty("api.key", "default-value"); 
}

总结对比表

场景能否解决循环依赖推荐方案
字段/Setter 注入✅(三级缓存)可选依赖、快速开发
构造器注入❌(直接报错)强制依赖、核心业务逻辑
@Value 注入配置不涉及循环依赖加默认值(${key:default}
多例 Bean 循环依赖❌(直接报错)避免多例 Bean 互相依赖

最佳实践

  1. 优先用构造器注入(强制依赖 + 不可变性)。
  2. 循环依赖改用 Setter/字段注入 或 @Lazy
  3. @Value 务必设置默认值,避免因配置缺失导致启动失败。

到此这篇关于Spring 依赖注入与循环依赖总结的文章就介绍到这了,更多相关Spring 依赖注入与循环依赖内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文