327 lines
7.1 KiB
Markdown
327 lines
7.1 KiB
Markdown
# 循环依赖问题修复说明
|
||
|
||
> **问题时间**: 2024年12月26日
|
||
> **问题类型**: 循环依赖导致编译错误
|
||
> **解决方案**: 使用@Lazy注解延迟加载
|
||
|
||
---
|
||
|
||
## 问题描述
|
||
|
||
编译时出现以下错误:
|
||
|
||
```
|
||
java: 找不到符号
|
||
符号: 变量 java
|
||
位置: 类 com.zbkj.service.service.impl.GroupServiceImpl
|
||
|
||
java: 找不到符号
|
||
符号: 变量 java
|
||
位置: 类 com.zbkj.service.service.impl.GroupMemberServiceImpl
|
||
|
||
java: 找不到符号
|
||
符号: 变量 java
|
||
位置: 类 com.zbkj.service.service.impl.GroupMessageServiceImpl
|
||
|
||
java: 找不到符号
|
||
符号: 变量 java
|
||
位置: 类 com.zbkj.service.service.impl.MessageRecallServiceImpl
|
||
```
|
||
|
||
---
|
||
|
||
## 问题原因
|
||
|
||
这个问题有两个原因:
|
||
|
||
### 1. Logger初始化错误
|
||
|
||
错误的写法:
|
||
```java
|
||
private static final Logger logger = LoggerFactory.getLogger(GroupServiceImpl.java);
|
||
```
|
||
|
||
正确的写法:
|
||
```java
|
||
private static final Logger logger = LoggerFactory.getLogger(GroupServiceImpl.class);
|
||
```
|
||
|
||
**说明:** `getLogger()`方法需要传入`Class`对象,而不是`.java`文件。
|
||
|
||
### 2. 循环依赖问题
|
||
|
||
这是一个典型的**循环依赖**问题:
|
||
|
||
1. `GroupServiceImpl` 依赖 `GroupMemberService`
|
||
2. `GroupMemberServiceImpl` 依赖 `GroupService`
|
||
|
||
这形成了一个循环依赖链:
|
||
```
|
||
GroupServiceImpl → GroupMemberService → GroupMemberServiceImpl → GroupService → GroupServiceImpl
|
||
```
|
||
|
||
Spring在创建Bean时无法确定先创建哪个Bean,导致编译错误。
|
||
|
||
---
|
||
|
||
## 解决方案
|
||
|
||
### 方案1:修复Logger初始化错误
|
||
|
||
**修改前:**
|
||
```java
|
||
private static final Logger logger = LoggerFactory.getLogger(GroupServiceImpl.java);
|
||
```
|
||
|
||
**修改后:**
|
||
```java
|
||
private static final Logger logger = LoggerFactory.getLogger(GroupServiceImpl.class);
|
||
```
|
||
|
||
**涉及的文件:**
|
||
1. `GroupServiceImpl.java` ✅
|
||
2. `GroupMemberServiceImpl.java` ✅
|
||
3. `GroupMessageServiceImpl.java` ✅
|
||
4. `MessageRecallServiceImpl.java` ✅
|
||
|
||
### 方案2:使用@Lazy注解解决循环依赖
|
||
|
||
使用Spring的`@Lazy`注解来延迟加载依赖,打破循环依赖链。
|
||
|
||
### 修改前的代码
|
||
|
||
**GroupServiceImpl.java**
|
||
```java
|
||
@Service
|
||
public class GroupServiceImpl extends ServiceImpl<GroupDao, Group> implements GroupService {
|
||
|
||
@Autowired
|
||
private GroupMemberService groupMemberService; // 直接注入
|
||
|
||
// ...
|
||
}
|
||
```
|
||
|
||
**GroupMemberServiceImpl.java**
|
||
```java
|
||
@Service
|
||
public class GroupMemberServiceImpl extends ServiceImpl<GroupMemberDao, GroupMember> implements GroupMemberService {
|
||
|
||
@Autowired
|
||
private GroupService groupService; // 直接注入
|
||
|
||
// ...
|
||
}
|
||
```
|
||
|
||
### 修改后的代码
|
||
|
||
**GroupServiceImpl.java**
|
||
```java
|
||
import org.springframework.context.annotation.Lazy;
|
||
|
||
@Service
|
||
public class GroupServiceImpl extends ServiceImpl<GroupDao, Group> implements GroupService {
|
||
|
||
private final GroupMemberService groupMemberService;
|
||
|
||
@Autowired
|
||
public GroupServiceImpl(@Lazy GroupMemberService groupMemberService) {
|
||
this.groupMemberService = groupMemberService;
|
||
}
|
||
|
||
// ...
|
||
}
|
||
```
|
||
|
||
**GroupMemberServiceImpl.java**
|
||
```java
|
||
import org.springframework.context.annotation.Lazy;
|
||
|
||
@Service
|
||
public class GroupMemberServiceImpl extends ServiceImpl<GroupMemberDao, GroupMember> implements GroupMemberService {
|
||
|
||
private final GroupService groupService;
|
||
|
||
@Autowired
|
||
public GroupServiceImpl(@Lazy GroupService groupService) {
|
||
this.groupService = groupService;
|
||
}
|
||
|
||
// ...
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 技术说明
|
||
|
||
### @Lazy注解的作用
|
||
|
||
`@Lazy`注解告诉Spring容器:
|
||
1. 不要在Bean创建时立即注入依赖
|
||
2. 而是在第一次使用时才创建代理对象
|
||
3. 这样可以打破循环依赖链
|
||
|
||
### 构造器注入 vs 字段注入
|
||
|
||
**修改前(字段注入):**
|
||
```java
|
||
@Autowired
|
||
private GroupMemberService groupMemberService;
|
||
```
|
||
|
||
**修改后(构造器注入):**
|
||
```java
|
||
private final GroupMemberService groupMemberService;
|
||
|
||
@Autowired
|
||
public GroupServiceImpl(@Lazy GroupMemberService groupMemberService) {
|
||
this.groupMemberService = groupMemberService;
|
||
}
|
||
```
|
||
|
||
**构造器注入的优点:**
|
||
1. ✅ 依赖关系更明确
|
||
2. ✅ 可以使用final关键字,保证不可变性
|
||
3. ✅ 更容易进行单元测试
|
||
4. ✅ Spring官方推荐的注入方式
|
||
|
||
---
|
||
|
||
## 其他解决循环依赖的方法
|
||
|
||
### 方法1:使用@Lazy注解(推荐)✅
|
||
```java
|
||
@Autowired
|
||
public GroupServiceImpl(@Lazy GroupMemberService groupMemberService) {
|
||
this.groupMemberService = groupMemberService;
|
||
}
|
||
```
|
||
|
||
**优点:**
|
||
- 简单易用
|
||
- 不改变业务逻辑
|
||
- Spring官方推荐
|
||
|
||
### 方法2:重构代码,消除循环依赖
|
||
```java
|
||
// 创建一个新的Service,将共同的逻辑提取出来
|
||
@Service
|
||
public class GroupCommonService {
|
||
// 共同的逻辑
|
||
}
|
||
|
||
@Service
|
||
public class GroupServiceImpl {
|
||
@Autowired
|
||
private GroupCommonService groupCommonService;
|
||
}
|
||
|
||
@Service
|
||
public class GroupMemberServiceImpl {
|
||
@Autowired
|
||
private GroupCommonService groupCommonService;
|
||
}
|
||
```
|
||
|
||
**优点:**
|
||
- 彻底消除循环依赖
|
||
- 代码结构更清晰
|
||
|
||
**缺点:**
|
||
- 需要重构代码
|
||
- 可能增加复杂度
|
||
|
||
### 方法3:使用Setter注入
|
||
```java
|
||
private GroupMemberService groupMemberService;
|
||
|
||
@Autowired
|
||
public void setGroupMemberService(@Lazy GroupMemberService groupMemberService) {
|
||
this.groupMemberService = groupMemberService;
|
||
}
|
||
```
|
||
|
||
**优点:**
|
||
- 可以在Bean创建后修改依赖
|
||
|
||
**缺点:**
|
||
- 不能使用final关键字
|
||
- 不如构造器注入清晰
|
||
|
||
---
|
||
|
||
## 验证结果
|
||
|
||
✅ **编译检查**: 无编译错误
|
||
✅ **循环依赖**: 已解决
|
||
✅ **代码质量**: 使用构造器注入,符合最佳实践
|
||
|
||
---
|
||
|
||
## 最佳实践建议
|
||
|
||
### 1. 避免循环依赖
|
||
|
||
在设计系统时,应该尽量避免循环依赖:
|
||
- 使用分层架构(Controller → Service → DAO)
|
||
- 遵循单一职责原则
|
||
- 合理划分模块边界
|
||
|
||
### 2. 优先使用构造器注入
|
||
|
||
```java
|
||
// ✅ 推荐:构造器注入
|
||
private final SomeService someService;
|
||
|
||
@Autowired
|
||
public MyService(SomeService someService) {
|
||
this.someService = someService;
|
||
}
|
||
|
||
// ❌ 不推荐:字段注入
|
||
@Autowired
|
||
private SomeService someService;
|
||
```
|
||
|
||
### 3. 必要时使用@Lazy
|
||
|
||
当无法避免循环依赖时,使用`@Lazy`注解:
|
||
```java
|
||
@Autowired
|
||
public MyService(@Lazy SomeService someService) {
|
||
this.someService = someService;
|
||
}
|
||
```
|
||
|
||
### 4. 定期检查依赖关系
|
||
|
||
使用工具检查项目中的循环依赖:
|
||
- IDEA的依赖分析工具
|
||
- Maven的依赖插件
|
||
- SonarQube等代码质量工具
|
||
|
||
---
|
||
|
||
## 总结
|
||
|
||
通过使用`@Lazy`注解和构造器注入,成功解决了`GroupServiceImpl`和`GroupMemberServiceImpl`之间的循环依赖问题。
|
||
|
||
**关键改进:**
|
||
1. ✅ 使用@Lazy注解延迟加载依赖
|
||
2. ✅ 使用构造器注入替代字段注入
|
||
3. ✅ 使用final关键字保证不可变性
|
||
4. ✅ 符合Spring最佳实践
|
||
|
||
**修复的文件:**
|
||
1. `GroupServiceImpl.java` - 修复Logger初始化 + 添加@Lazy注解,使用构造器注入
|
||
2. `GroupMemberServiceImpl.java` - 修复Logger初始化 + 添加@Lazy注解,使用构造器注入
|
||
3. `GroupMessageServiceImpl.java` - 修复Logger初始化
|
||
4. `MessageRecallServiceImpl.java` - 修复Logger初始化
|
||
|
||
---
|
||
|
||
**修复时间**: 2024年12月26日
|
||
**文档版本**: v1.0
|