文章目录
- 1. 什么是循环依赖?
- 1.1 概念解析
- 1.2 示例代码
- 2. 循环依赖的类型
- 2.1 构造器循环依赖(不可解决 ❌)
- 2.2 Setter 方式或 `@Autowired` 方式的循环依赖(可解决 ✅)
- 3. 解决循环依赖的方式
- 3.1 方式一:使用 `@Lazy`(延迟加载) 🚀
- 3.2 方式二:修改依赖关系 🛠️
- 3.3 方式三:使用 `@PostConstruct` 进行初始化 🎯
- 3.4 方式四:使用 `ApplicationContextAware` 直接获取 Bean ⚙️
- 4. 结论与最佳实践 🏆
1. 什么是循环依赖?
1.1 概念解析
在 Spring 容器中,循环依赖(Circular Dependency) 是指:
- Bean A 依赖 Bean B
- Bean B 依赖 Bean A
- 这种情况下,Spring 在创建 Bean 时会出现互相等待,可能导致异常。
1.2 示例代码
@Component
public class A {private final B b;@Autowiredpublic A(B b) {this.b = b;}
}@Component
public class B {private final A a;@Autowiredpublic B(A a) {this.a = a;}
}
💥 错误信息:
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'A': Requested bean is currently in creation:
Is there an unresolvable circular reference?
2. 循环依赖的类型
2.1 构造器循环依赖(不可解决 ❌)
- 通过构造方法互相依赖,Spring 无法提前暴露 Bean 实例,因此会直接报错。
- 示例:(上面代码就属于此类)
2.2 Setter 方式或 @Autowired
方式的循环依赖(可解决 ✅)
- Spring 默认支持这种循环依赖(单例模式下)。
- 示例:
@Component
public class A {private B b;@Autowiredpublic void setB(B b) {this.b = b;}
}@Component
public class B {private A a;@Autowiredpublic void setA(A a) {this.a = a;}
}
📌 Spring 解决方式:
- 一级缓存(singletonObjects): 存放完全实例化的 Bean。
- 二级缓存(earlySingletonObjects): 存放提前暴露的半成品 Bean。
- 三级缓存(singletonFactories): 存放创建 Bean 的工厂,以支持代理(如 AOP 代理)。
3. 解决循环依赖的方式
3.1 方式一:使用 @Lazy
(延迟加载) 🚀
- 只在需要时才创建 Bean,避免初始化时发生循环依赖。
@Component
public class A {private final B b;@Autowiredpublic A(@Lazy B b) {this.b = b;}
}
✅ 适用于构造器注入,但不能解决所有情况。
3.2 方式二:修改依赖关系 🛠️
- 重新设计 Bean 关系,避免直接互相依赖。
- 可以通过 引入中间层(如事件监听模式、消息队列等) 解决。
3.3 方式三:使用 @PostConstruct
进行初始化 🎯
- 让 Spring 先创建 Bean,然后在初始化时再注入依赖。
@Component
public class A {private B b;@Autowiredpublic A() {}@PostConstructpublic void init() {this.b = SpringContextUtil.getBean(B.class);}
}
✅ 避免构造方法循环依赖,适用于某些场景。
3.4 方式四:使用 ApplicationContextAware
直接获取 Bean ⚙️
@Component
public class A implements ApplicationContextAware {private B b;private ApplicationContext applicationContext;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}@PostConstructpublic void init() {this.b = applicationContext.getBean(B.class);}
}
✅ 适用于特定场景,但不推荐滥用 ApplicationContext,影响 Spring 设计模式。
4. 结论与最佳实践 🏆
方案 | 适用情况 | 备注 |
---|---|---|
@Lazy | 适用于构造器循环依赖 | 延迟加载可能会影响性能 |
修改依赖结构 | 推荐 | 最优方案,从根本上解决问题 |
@PostConstruct | 适用于部分场景 | 但可能导致依赖获取不直观 |
ApplicationContext 获取 Bean | 可行但不推荐 | 破坏 Spring 设计模式 |
🎯 最佳实践:
- 避免构造器循环依赖,优先使用
@Autowired
方式。 - 重构业务逻辑,减少不必要的循环依赖。
- 若无法避免,尝试
@Lazy
、@PostConstruct
方案。 - **尽量不使用
ApplicationContextAware
,防止耦合过高。 **