Spring Bean 循环依赖详解
什么是循环依赖?
循环依赖是指两个或多个 Bean 相互依赖,形成一个依赖环:
```
Bean A → 依赖 → Bean B
↑ ↓
← 依赖 ←
```
代码示例
```java
// 情况1:构造函数循环依赖(Spring无法解决)
@Service
public class ServiceA {
private final ServiceB serviceB;
@Autowired
public ServiceA(ServiceB serviceB) { // 构造器注入
this.serviceB = serviceB;
}
}
@Service
public class ServiceB {
private final ServiceA serviceA;
@Autowired
public ServiceB(ServiceA serviceA) { // 构造器注入
this.serviceA = serviceA;
}
}
```
```java
// 情况2:属性/Setter注入循环依赖(Spring可以解决)
@Service
public class UserService {
@Autowired
private RoleService roleService; // 属性注入
public void createUser() {
roleService.assignRole();
}
}
@Service
public class RoleService {
@Autowired
private UserService userService; // 属性注入
public void assignRole() {
userService.createUser();
}
}
```
什么时候会遇到循环依赖?
1. 架构设计问题
```java
// 反例:紧密耦合的业务层
@Service
public class OrderService {
@Autowired
private PaymentService paymentService;
public void createOrder() {
// 订单需要支付
paymentService.processPayment();
}
}
@Service
public class PaymentService {
@Autowired
private OrderService orderService;
public void processPayment() {
// 支付需要订单信息
orderService.updateOrderStatus(); // 设计问题!
}
}
```
2. 缓存与业务逻辑耦合
```java
@Service
public class ProductService {
@Autowired
private CacheService cacheService;
public Product getProduct(Long id) {
return cacheService.getProduct(id);
}
}
@Service
public class CacheService {
@Autowired
private ProductService productService; // 需要从数据库加载
public Product getProduct(Long id) {
// 缓存未命中时调用 ProductService
return productService.loadFromDB(id);
}
}
```
3. 事件监听器相互调用
```java
@Component
public class OrderEventListener {
@Autowired
private EmailService emailService;
@EventListener
public void handleOrderEvent(OrderEvent event) {
emailService.sendNotification(event);
}
}
@Component
public class EmailService {
@Autowired
private OrderEventListener orderEventListener; // 错误的设计
public void sendNotification(OrderEvent event) {
// 发送邮件后触发其他事件
orderEventListener.onEmailSent(event);
}
}
```
4. 配置类相互依赖
```java
@Configuration
public class ConfigA {
@Autowired
private ConfigB configB;
@Bean
public ServiceA serviceA() {
return new ServiceA(configB.serviceB());
}
}
@Configuration
public class ConfigB {
@Autowired
private ConfigA configA;
@Bean
public ServiceB serviceB() {
return new ServiceB(configA.serviceA());
}
}
```
5. 常见的实际场景
```java
// 场景1:权限验证
@Service
public class SecurityService {
@Autowired
private UserService userService; // 需要验证用户
public boolean hasPermission(User user, String permission) {
return userService.checkPermission(user, permission);
}
}
@Service
public class UserService {
@Autowired
private SecurityService securityService; // 需要安全验证
@PreAuthorize("hasRole('ADMIN')")
public User updateUser(User user) {
// 更新用户
}
}
// 场景2:消息处理链
@Service
public class MessageHandlerA {
@Autowired
private MessageHandlerB nextHandler;
public void handle(Message msg) {
// 处理消息
nextHandler.handle(msg);
}
}
@Service
public class MessageHandlerB {
@Autowired
private MessageHandlerA previousHandler; // 需要回溯
public void handle(Message msg) {
if (msg.needsRetry()) {
previousHandler.handle(msg);
}
}
}
```
Spring 如何解决循环依赖?
三级缓存机制
```java
// Spring 内部的三级缓存
public class DefaultSingletonBeanRegistry {
// 第一级缓存:完整的单例 Bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 第二级缓存:提前暴露的早期引用(半成品)
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
// 第三级缓存:对象工厂,用于创建代理对象
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
}
```
解决流程(属性注入)
```
创建 A → 实例化 A → 放入三级缓存 → 填充属性(需要 B)
创建 B → 实例化 B → 放入三级缓存 → 填充属性(需要 A)
↓
从三级缓存获取 A 的早期引用 → 注入给 B → 完成 B
↓
B 返回给 A → A 完成初始化 → 移入一级缓存
```
解决方案
方案1:重新设计(首选)
```java
// 坏的设计:循环依赖
@Service
public class OrderService {
@Autowired
private PaymentService paymentService;
}
@Service
public class PaymentService {
@Autowired
private OrderService orderService;
}
// 好的设计:提取公共逻辑
@Service
public class OrderService {
private final OrderProcessor orderProcessor;
public OrderService(OrderProcessor processor) {
this.orderProcessor = processor;
}
}
@Service
public class PaymentService {
private final OrderProcessor orderProcessor;
public PaymentService(OrderProcessor processor) {
this.orderProcessor = processor;
}
}
@Component
public class OrderProcessor {
// 公共业务逻辑
public void processOrder(Order order) {
// 处理订单
}
}
```
方案2:使用 Setter/方法注入
```java
@Service
public class ServiceA {
private ServiceB serviceB;
@Autowired
public void setServiceB(ServiceB serviceB) { // Setter注入
this.serviceB = serviceB;
}
}
@Service
public class ServiceB {
private ServiceA serviceA;
@Autowired
public void setServiceA(ServiceA serviceA) { // Setter注入
this.serviceA = serviceA;
}
}
```
方案3:使用 @Lazy
```java
@Service
public class ServiceA {
private final ServiceB serviceB;
@Autowired
public ServiceA(@Lazy ServiceB serviceB) { // 延迟加载
this.serviceB = serviceB;
}
}
@Service
public class ServiceB {
private final ServiceA serviceA;
@Autowired
public ServiceB(ServiceA serviceA) {
this.serviceA = serviceA;
}
}
```
方案4:使用 ApplicationContext
```java
@Service
public class ServiceA implements ApplicationContextAware {
private ApplicationContext context;
private ServiceB serviceB;
@Override
public void setApplicationContext(ApplicationContext context) {
this.context = context;
}
@PostConstruct
public void init() {
this.serviceB = context.getBean(ServiceB.class); // 手动获取
}
}
@Service
public class ServiceB {
@Autowired
private ServiceA serviceA; // 正常注入
}
```
方案5:使用 ObjectProvider(Spring 4.3+)
```java
@Service
public class ServiceA {
private final ObjectProvider<ServiceB> serviceBProvider;
@Autowired
public ServiceA(ObjectProvider<ServiceB> serviceBProvider) {
this.serviceBProvider = serviceBProvider;
}
public void doSomething() {
ServiceB serviceB = serviceBProvider.getIfAvailable(); // 按需获取
if (serviceB != null) {
serviceB.process();
}
}
}
@Service
public class ServiceB {
@Autowired
private ServiceA serviceA;
}
```
方案6:接口分离
```java
// 定义接口
public interface IOrderService {
void createOrder();
}
public interface IPaymentService {
void processPayment();
}
// 实现类1
@Service
public class OrderService implements IOrderService {
private final IPaymentService paymentService;
@Autowired
public OrderService(IPaymentService paymentService) {
this.paymentService = paymentService;
}
}
// 实现类2
@Service
public class PaymentService implements IPaymentService {
private final IOrderService orderService;
@Autowired
public PaymentService(IOrderService orderService) {
this.orderService = orderService;
}
}
// 配置类解决循环依赖
@Configuration
public class ServiceConfig {
@Bean
@DependsOn("paymentService") // 明确依赖顺序
public IOrderService orderService(IPaymentService paymentService) {
return new OrderService(paymentService);
}
@Bean
public IPaymentService paymentService() {
return new PaymentService(null); // 先创建,后设置
}
@Bean
public Object postProcess(IPaymentService paymentService,
IOrderService orderService) {
// 通过后处理器设置依赖
((PaymentService) paymentService).setOrderService(orderService);
return null;
}
}
```
Spring Boot 2.6+ 的变化
从 Spring Boot 2.6 开始,默认禁止了循环依赖:
```yaml
# application.yml
spring:
main:
allow-circular-references: false # 默认false,禁止循环依赖
```
```java
// 如果需要允许循环依赖(不推荐)
@SpringBootApplication
public class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class)
.allowCircularReferences(true) // 允许循环依赖
.run(args);
}
}
```
最佳实践和检测工具
1. 使用架构检测工具
```xml
<!-- ArchUnit 依赖 -->
<dependency>
<groupId>com.tngtech.archunit</groupId>
<artifactId>archunit-junit5</artifactId>
<version>1.0.0</version>
<scope>test</scope>
</dependency>
```
```java
// 测试代码,检测循环依赖
@AnalyzeClasses(packages = "com.example")
public class ArchitectureTest {
@Test
public void shouldHaveNoCyclicDependencies() {
slices()
.matching("com.example.(*)..")
.should().beFreeOfCycles(); // 检测循环依赖
}
@Test
public void serviceLayerShouldNotHaveCycles() {
SliceAssignment assignment = new SliceAssignment() {
@Override
public String getDescription() {
return "按服务划分";
}
@Override
public String apply(JavaClass javaClass) {
if (javaClass.getPackageName().contains(".service.")) {
return javaClass.getPackageName();
}
return null;
}
};
ArchRule rule = slices().assignedFrom(assignment)
.should().beFreeOfCycles();
rule.check(importedClasses);
}
}
```
2. IDE 插件检测
· IntelliJ IDEA: 内置循环依赖检测
· Eclipse: 安装相关架构插件
· SonarQube: 代码质量检测
3. 代码审查清单
```java
// 审查点1:构造函数注入
@Service
public class BadExample {
@Autowired
private AnotherService service; // 属性注入,可能隐藏循环依赖
public BadExample() {
// 空构造器
}
}
@Service
public class GoodExample {
private final AnotherService service;
public GoodExample(AnotherService service) { // 构造器注入
this.service = service; // 立即发现循环依赖
}
}
// 审查点2:依赖方向
// 从高层到底层:Controller → Service → Repository
// 不要出现反向依赖
```
常见问题排查
错误信息示例
```
Error creating bean with name 'serviceA':
Requested bean is currently in creation: Is there an unresolvable circular reference?
```
排查步骤
```java
// 1. 查看 Bean 创建日志
@Component
public class BeanCreationLogger implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
System.out.println("Creating bean: " + beanName);
return bean;
}
}
// 2. 使用 Spring Boot Actuator
// application.yml
management:
endpoints:
web:
exposure:
include: beans
// 访问:http://localhost:8080/actuator/beans
// 3. 使用调试模式
@Component
public class CircularDependencyDetector implements SmartInitializingSingleton {
@Autowired
private ConfigurableListableBeanFactory beanFactory;
@Override
public void afterSingletonsInstantiated() {
String[] beanNames = beanFactory.getBeanDefinitionNames();
for (String beanName : beanNames) {
BeanDefinition bd = beanFactory.getBeanDefinition(beanName);
if (bd.isSingleton()) {
try {
Object bean = beanFactory.getBean(beanName);
System.out.println("成功创建: " + beanName);
} catch (BeanCurrentlyInCreationException e) {
System.err.println("循环依赖检测到: " + beanName);
System.err.println(e.getMessage());
}
}
}
}
}
```
总结
解决方案 适用场景 优点 缺点
重新设计 所有场景 根本解决,代码清晰 需要重构
Setter注入 简单循环 Spring支持,简单 破坏不变性
@Lazy 构造函数注入 延迟加载,避免初始化死锁 可能隐藏设计问题
ApplicationContext 复杂场景 灵活控制 增加耦合
ObjectProvider Spring 4.3+ 推荐方式,类型安全 需要手动获取
接口分离 架构优化 符合设计原则 增加接口数量
黄金法则:
1. 优先使用构造器注入,它能尽早暴露循环依赖
2. 循环依赖通常是设计问题的信号,应该首先考虑重构
3. 如果必须存在循环依赖,使用 @Lazy 或 Setter注入
4. 在 Spring Boot 2.6+ 中,默认禁止循环依赖,这是为了鼓励更好的设计