Spring Boot 2.6+默认禁止了循环依赖

你遇到了Spring Boot 2.6+的一个**重要变化**!在Spring Boot 2.6及更高版本中,**默认禁止了循环依赖**。

## 问题原因

从Spring Boot 2.6开始,为了推动更好的代码实践,Spring团队**默认禁用了循环依赖**。即使你使用setter注入,Spring也不会尝试解决循环依赖问题。

## 解决方案

### 方案1:启用循环依赖(最简单,但不推荐用于生产)
在`application.properties`或`application.yml`中:

```properties
# application.properties
spring.main.allow-circular-references=true
```

```yaml
# application.yml
spring:
main:
allow-circular-references: true
```

### 方案2:使用@Lazy注解(推荐)
这是Spring Boot 2.6+推荐的解决方案:

```java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

@Component
public class ServiceA {
private ServiceB serviceB;

@Autowired
public void setServiceB(@Lazy ServiceB serviceB) {
// 或直接 @Lazy ServiceB serviceB(字段注入)
this.serviceB = serviceB;
}
}
```

```java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class ServiceB {
private ServiceA serviceA;

@Autowired
public void setServiceA(ServiceA serviceA) {
this.serviceA = serviceA;
}
}
```

**注意**:通常只需要在一侧加`@Lazy`就能解决问题。

### 方案3:重构代码(最佳实践)
重新设计你的类结构,消除循环依赖:

#### 方案3.1:提取共同逻辑到第三个类
```java
@Component
public class CommonService {
// 共同的业务逻辑
}

@Component
public class ServiceA {
@Autowired
private CommonService commonService;
}

@Component
public class ServiceB {
@Autowired
private CommonService commonService;
}
```

#### 方案3.2:使用接口分离
```java
public interface IServiceA {
void doSomething();
}

public interface IServiceB {
void doSomethingElse();
}

@Component
public class ServiceA implements IServiceA {
private IServiceB serviceB;

@Autowired
public ServiceA(@Lazy IServiceB serviceB) {
this.serviceB = serviceB;
}
}

@Component
public class ServiceB implements IServiceB {
private IServiceA serviceA;

@Autowired
public ServiceB(IServiceA serviceA) {
this.serviceA = serviceA;
}
}
```

#### 方案3.3:使用ApplicationContext延迟获取
```java
@Component
public class ServiceA {
@Autowired
private ApplicationContext context;
private ServiceB serviceB;

@PostConstruct
public void init() {
serviceB = context.getBean(ServiceB.class);
}
}
```

## 为什么Spring Boot要禁止循环依赖?

Spring团队做出这个改变有几个原因:

1. **代码质量**:循环依赖通常是设计问题的标志
2. **测试困难**:有循环依赖的类难以独立测试
3. **启动性能**:解析循环依赖需要额外的处理
4. **可维护性**:循环依赖使代码更难以理解和修改

## 实际建议

1. **短期解决**:在测试/开发环境中,可以临时使用`spring.main.allow-circular-references=true`
2. **中期解决**:在生产代码中使用`@Lazy`注解
3. **长期解决**:重构代码,消除循环依赖

```java
// 生产代码推荐使用的方式
@Component
public class ServiceA {
@Autowired
@Lazy // 明确表示这是延迟初始化的依赖
private ServiceB serviceB;

// 或者使用构造器注入 + @Lazy
// public ServiceA(@Lazy ServiceB serviceB) {
// this.serviceB = serviceB;
// }
}
```

记住,虽然Spring提供了解决循环依赖的机制,但最好的架构设计应该是**避免循环依赖**,这会使得你的代码更加清晰、可测试和可维护。

posted @ 2026-01-05 02:09  人在代码在  阅读(1)  评论(0)    收藏  举报