
1. 什么是循环依赖
循环依赖是指两个或多个bean之间相互依赖,形成闭环依赖关系,spring容器在创建bean过程中会出现依赖死锁问题。spring中单例bean、属性注入场景可通过机制解决部分循环依赖,部分场景无法解决。
循环依赖的三种场景
场景1:双向直接循环依赖(最常见)
@component
public class beana {
@autowired
private beanb beanb;
}
@component
public class beanb {
@autowired
private beana beana;
}
场景2:多bean间接循环依赖
@component
public class servicea {
@autowired
private serviceb serviceb;
}
@component
public class serviceb {
@autowired
private servicec servicec;
}
@component
public class servicec {
@autowired
private servicea servicea;
}
场景3:bean自我依赖(特殊场景)
@component
public class selfdependentbean {
// 自身依赖自身,普通场景无问题,代理场景会触发循环依赖校验
@autowired
private selfdependentbean self;
}
2. spring解决循环依赖的核心机制
spring 核心通过三级缓存机制解决单例bean、属性/setter注入场景下的循环依赖,核心思想:提前暴露未完全初始化的bean早期引用,打破依赖闭环。
三级缓存底层源码结构
public class defaultsingletonbeanregistry extends simplealiasregistry implements singletonbeanregistry {
// 一级缓存:完整初始化完毕的单例bean(可直接使用)
private final map<string, object> singletonobjects = new concurrenthashmap<>(256);
// 二级缓存:早期bean引用(已实例化、未填充属性、未初始化)
private final map<string, object> earlysingletonobjects = new concurrenthashmap<>(16);
// 三级缓存:bean工厂对象,用于生成早期引用、处理aop代理
private final map<string, objectfactory<?>> singletonfactories = new hashmap<>(16);
// 标记正在创建中的bean,用于循环依赖预判
private final set<string> singletonscurrentlyincreation = collections.newsetfrommap(new concurrenthashmap<>(16));
}
三级缓存核心作用总结
- 一级缓存(singletonobjects):存储完全初始化、属性填充完毕、可直接使用的单例bean,全局唯一。
- 二级缓存(earlysingletonobjects):缓存早期bean实例/代理对象,避免重复执行三级缓存的代理创建逻辑,提升性能。
- 三级缓存(singletonfactories):核心关键,存储bean的objectfactory工厂,可动态返回原始bean或aop代理bean,保证依赖注入一致性。
3. 循环依赖完整解决流程详解
以 beana、beanb双向循环依赖 为例,完整还原spring容器创建、缓存流转、依赖注入全过程。
测试案例代码
@component
public class beana {
@autowired
private beanb beanb;
public beana() {
system.out.println("beana 构造函数执行");
}
@postconstruct
public void init() {
system.out.println("beana @postconstruct 执行");
}
}
@component
public class beanb {
@autowired
private beana beana;
public beanb() {
system.out.println("beanb 构造函数执行");
}
@postconstruct
public void init() {
system.out.println("beanb @postconstruct 执行");
}
}
分步执行流程
步骤1:容器启动,开始创建beana,将beana标记为「正在创建」
步骤2:执行beana构造函数,完成实例化(空对象,无属性)
步骤3:将beana的objectfactory存入三级缓存,提前暴露早期引用(支持后续依赖获取、aop代理)
步骤4:进入属性填充阶段,检测到beana依赖beanb,触发beanb创建
步骤5:标记beanb为「正在创建」,执行构造函数完成实例化
步骤6:将beanb的objectfactory存入三级缓存
步骤7:填充beanb属性,检测到依赖beana
步骤8:从缓存获取beana:一级缓存无、二级缓存无,从三级缓存获取objectfactory,生成beana早期引用
步骤9:将beana早期引用存入二级缓存,删除三级缓存对应数据
步骤10:beanb成功注入beana早期引用,完成属性填充、@postconstruct初始化
步骤11:beanb完全创建完成,存入一级缓存,清空二、三级缓存数据
步骤12:beana成功注入完整的beanb实例,完成自身属性填充、初始化
步骤13:beana创建完成,存入一级缓存,清空对应缓存数据,循环依赖彻底解决
核心流程源码模拟
public class circulardependencysimulator {
// 模拟spring三级缓存
private map<string, object> singletonobjects = new hashmap<>(); // 一级缓存
private map<string, object> earlysingletonobjects = new hashmap<>(); // 二级缓存
private map<string, objectfactory> singletonfactories = new hashmap<>();// 三级缓存
private set<string> singletonsincreation = new hashset<>(); // 创建中bean标记
public object getbean(string beanname) {
// 1. 优先从一级缓存获取完整bean
object bean = singletonobjects.get(beanname);
if (bean != null) {
return bean;
}
// 2. 判断是否存在循环依赖(bean正在创建中)
if (singletonsincreation.contains(beanname)) {
// 3. 从二级缓存获取早期引用
bean = earlysingletonobjects.get(beanname);
if (bean == null) {
// 4. 二级缓存无,从三级缓存工厂创建早期引用
objectfactory factory = singletonfactories.get(beanname);
if (factory != null) {
bean = factory.getobject();
// 三级缓存升级至二级缓存,提升复用效率
earlysingletonobjects.put(beanname, bean);
singletonfactories.remove(beanname);
}
}
return bean;
}
// 5. 无循环依赖,正常创建bean
return createbean(beanname);
}
private object createbean(string beanname) {
// 标记bean正在创建
singletonsincreation.add(beanname);
// 1. 实例化bean(构造方法)
object bean = instantiatebean(beanname);
// 2. 存入三级缓存:提前暴露工厂,解决循环依赖+aop代理
singletonfactories.put(beanname, () -> {
// 普通bean返回原实例,aop场景返回代理对象
return bean;
});
// 3. 填充属性(触发依赖注入、循环依赖检测)
populatebean(beanname, bean);
// 4. 执行初始化方法(@postconstruct、init-method)
initializebean(beanname, bean);
// 5. 完全初始化完毕,升级至一级缓存,清空临时缓存
singletonobjects.put(beanname, bean);
earlysingletonobjects.remove(beanname);
singletonfactories.remove(beanname);
singletonsincreation.remove(beanname);
return bean;
}
// 模拟实例化、属性填充、初始化方法
private object instantiatebean(string beanname) { return null; }
private void populatebean(string beanname, object bean) {}
private void initializebean(string beanname, object bean) {}
}
4. 不同注入方式对循环依赖的支持
4.1 字段注入(@autowired)✅ 支持循环依赖
字段注入是spring解决循环依赖的主流场景,依托三级缓存提前暴露引用,完美打破依赖闭环。
@component
public class servicea {
@autowired
private serviceb serviceb;
}
@component
public class serviceb {
@autowired
private servicea servicea;
}
4.2 setter方法注入 ✅ 支持循环依赖
setter注入属于实例化后赋值,bean先完成构造实例化,再填充属性,可提前存入三级缓存,支持循环依赖。
@component
public class servicea {
private serviceb serviceb;
@autowired
public void setserviceb(serviceb serviceb) {
this.serviceb = serviceb;
}
}
@component
public class serviceb {
private servicea servicea;
@autowired
public void setservicea(servicea servicea) {
this.servicea = servicea;
}
}
4.3 构造器注入 ❌ 不支持循环依赖
构造器注入是实例化阶段强制依赖,bean创建前必须先注入依赖对象,无法提前暴露引用,三级缓存机制无法生效,直接抛出循环依赖异常。
@component
public class servicea {
private serviceb serviceb;
// 构造器注入:实例化时必须拥有完整serviceb,无法解决循环依赖
public servicea(serviceb serviceb) {
this.serviceb = serviceb;
}
}
@component
public class serviceb {
private servicea servicea;
public serviceb(servicea servicea) {
this.servicea = servicea;
}
}
核心原因:三级缓存的存入时机是构造方法执行完毕、实例化完成后,构造器注入在实例化阶段就需要依赖对象,无提前暴露机会。
5. 特殊场景循环依赖处理
5.1 prototype原型作用域 ❌ 不支持循环依赖
原型bean每次获取都会新建实例,spring不会缓存原型bean,无三级缓存机制,出现循环依赖直接抛出 beancurrentlyincreationexception。
@scope(configurablebeanfactory.scope_prototype)
@component
public class prototypea {
@autowired
private prototypeb prototypeb;
}
@scope(configurablebeanfactory.scope_prototype)
@component
public class prototypeb {
@autowired
private prototypea prototypea;
}
5.2 @lazy注解解决构造器循环依赖 ✅
通过 @lazy 延迟加载,注入代理对象,无需立即初始化目标bean,打破构造器循环依赖。
@component
public class servicea {
private serviceb serviceb;
// 延迟加载:注入代理对象,使用时才初始化真实bean
public servicea(@lazy serviceb serviceb) {
this.serviceb = serviceb;
}
}
@component
public class serviceb {
private servicea servicea;
public serviceb(servicea servicea) {
this.servicea = servicea;
}
}
5.3 @dependson控制bean加载顺序
手动指定bean加载优先级,提前加载依赖bean,规避循环依赖,但无法根治复杂闭环依赖。
@component
@dependson("serviceb") // 优先加载serviceb,再加载当前bean
public class servicea {
@autowired
private serviceb serviceb;
}
@component
public class serviceb {
@autowired
private servicea servicea;
}
6. aop代理与循环依赖
三级缓存存在的核心意义就是兼容aop代理场景,二级缓存无法动态生成代理对象,会导致依赖注入的对象与最终bean不一致。
带aop代理的循环依赖案例
@service
public class userservice {
@autowired
private orderservice orderservice;
@transactional // 触发aop动态代理
public void createuser() {
// 业务逻辑
}
}
@service
public class orderservice {
@autowired
private userservice userservice; // 注入的是userservice代理对象
public void createorder() {
userservice.createuser(); // 通过代理对象执行事务逻辑
}
}
aop代理处理核心流程
- 实例化原始bean对象(无代理)
- 将可生成代理对象的objectfactory存入三级缓存
- 其他bean依赖当前bean时,三级缓存工厂动态生成代理对象
- 保证全程注入、使用的都是代理bean,保持一致性,避免事务失效、功能异常
7. 高频面试题汇总
q1:spring核心如何解决单例bean循环依赖?
a:通过三级缓存机制提前暴露 bean早期引用解决:
- 一级缓存:存放完全初始化完成的单例bean;
- 二级缓存:存放已实例化未初始化的早期bean/代理引用;
- 三级缓存:存放bean工厂对象,支持动态生成原始bean或aop代理bean;
核心流程:实例化bean → 存入三级缓存提前暴露 → 填充属性解决依赖 → 初始化完成后升级至一级缓存。
q2:哪些循环依赖spring无法解决?
a:
- 构造器注入产生的循环依赖;
- prototype原型作用域bean的循环依赖;
- @async异步方法导致的自调用循环依赖;
- 多线程复杂嵌套循环依赖。
q3:为什么需要三级缓存,两级缓存不行?
a:核心为了兼容aop代理:
二级缓存只能存储固定对象,无法动态生成代理;三级缓存的objectfactory是函数式工厂,可在被依赖时动态判断是否需要代理,保证注入的bean和最终容器中的bean完全一致,解决代理不一致问题。普通无代理bean场景,两级缓存可解决循环依赖。
q4:三种注入方式循环依赖支持情况总结?
a:
- 字段注入、setter注入:支持(实例化后赋值,可提前暴露引用);
- 构造器注入:不支持(实例化阶段依赖前置,无缓存暴露时机)。
q5:业务中如何优雅避免循环依赖?
a:优先从设计层面解决,其次使用技术手段规避:接口解耦、事件驱动、延迟加载、上下文动态获取bean。
8. 循环依赖最佳解决方案与实践
8.1 架构设计层面根治(最优方案)
杜绝双向强依赖,基于接口解耦、参数传值代替直接依赖。
// 糟糕设计:双向紧密耦合,极易产生循环依赖
@service
public class baduserservice {
@autowired
private badorderservice orderservice;
}
@service
public class badorderservice {
@autowired
private baduserservice userservice;
}
// 优雅设计:接口解耦,单向依赖
public interface userservice { void createuser(); }
public interface orderservice { void createorder(long userid); }
@service
public class userserviceimpl implements userservice {
private final orderservice orderservice;
// 单向依赖接口,无双向闭环
public userserviceimpl(orderservice orderservice) {
this.orderservice = orderservice;
}
@override
public void createuser() {
// 业务逻辑
}
}
@service
public class orderserviceimpl implements orderservice {
// 通过参数获取用户信息,不直接依赖userservice
@override
public void createorder(long userid) {
// 业务逻辑
}
}
8.2 延迟获取bean(applicationcontext动态获取)
不直接注入bean,使用时从容器动态获取,规避启动时循环依赖。
@service
public class smartservicea {
private applicationcontext applicationcontext;
// 注入上下文,不注入业务bean
public smartservicea(applicationcontext applicationcontext) {
this.applicationcontext = applicationcontext;
}
public void dosomething() {
// 业务调用时动态获取bean
serviceb serviceb = applicationcontext.getbean(serviceb.class);
serviceb.process();
}
}
8.3 事件驱动解耦(推荐业务方案)
通过spring事件机制代替直接方法调用,彻底消除bean双向依赖。
@component
public class userservice {
@autowired
private applicationeventpublisher eventpublisher;
public void createuser(user user) {
// 核心业务
userrepository.save(user);
// 发布事件,解耦下游业务
eventpublisher.publishevent(new usercreatedevent(this, user.getid()));
}
}
@component
public class orderservice {
// 监听事件,被动触发业务,无直接依赖
@eventlistener
public void handleusercreated(usercreatedevent event) {
createdefaultorder(event.getuserid());
}
private void createdefaultorder(long userid) {
// 创建默认订单业务
}
}
到此这篇关于spring解决bean的循环依赖的实现示例的文章就介绍到这了,更多相关spring bean循环依赖内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论