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