352 lines
10 KiB
Markdown
352 lines
10 KiB
Markdown
|
# 快照功能实现方案对比
|
|||
|
|
|||
|
## 概述
|
|||
|
|
|||
|
本文档对比了三种不同的快照功能实现方案,从侵入性、自动化程度、维护性等角度进行分析。
|
|||
|
|
|||
|
## 方案对比
|
|||
|
|
|||
|
### 方案1:方法签名模式拦截(推荐)
|
|||
|
|
|||
|
#### 特点
|
|||
|
- **完全自动化**:通过方法签名模式自动识别和拦截
|
|||
|
- **零侵入性**:现有业务代码完全无需修改
|
|||
|
- **智能识别**:支持多种命名规范
|
|||
|
|
|||
|
#### 实现方式
|
|||
|
```java
|
|||
|
@Aspect
|
|||
|
@Component
|
|||
|
public class UserEntityAspect {
|
|||
|
|
|||
|
// 拦截所有返回User的查询方法
|
|||
|
@Around("execution(* com.example.refactor.service.*Service.getUser*(Long)) || " +
|
|||
|
"execution(* com.example.refactor.service.*Service.findUser*(Long)) || " +
|
|||
|
"execution(* com.example.refactor.service.*Service.queryUser*(Long))")
|
|||
|
public Object aroundUserQuery(ProceedingJoinPoint joinPoint) {
|
|||
|
// 自动应用快照逻辑
|
|||
|
}
|
|||
|
|
|||
|
// 拦截所有更新User的方法
|
|||
|
@Around("execution(* com.example.refactor.service.*Service.updateUser*(Long, ..)) || " +
|
|||
|
"execution(* com.example.refactor.service.*Service.modifyUser*(Long, ..)) || " +
|
|||
|
"execution(* com.example.refactor.service.*Service.editUser*(Long, ..))")
|
|||
|
public Object aroundUserUpdate(ProceedingJoinPoint joinPoint) {
|
|||
|
// 自动创建快照
|
|||
|
}
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
#### 业务代码(完全原样)
|
|||
|
```java
|
|||
|
@Service
|
|||
|
public class UserManagementService {
|
|||
|
|
|||
|
// 完全原样的业务代码,无需任何修改
|
|||
|
public User getUserById(Long id) {
|
|||
|
return userRepository.findById(id).orElse(null);
|
|||
|
}
|
|||
|
|
|||
|
public User updateUser(Long id, String name, String email, String phone) {
|
|||
|
User user = userRepository.findById(id).orElse(null);
|
|||
|
if (user != null) {
|
|||
|
user.setName(name);
|
|||
|
user.setEmail(email);
|
|||
|
user.setPhone(phone);
|
|||
|
user.setUpdateTime(new Date());
|
|||
|
return userRepository.save(user);
|
|||
|
}
|
|||
|
return null;
|
|||
|
}
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
#### 优势
|
|||
|
- ✅ 完全自动化,无需修改业务代码
|
|||
|
- ✅ 零侵入性,保持代码原样
|
|||
|
- ✅ 智能识别,支持多种命名规范
|
|||
|
- ✅ 易于扩展和维护
|
|||
|
- ✅ 性能开销小
|
|||
|
|
|||
|
#### 劣势
|
|||
|
- ⚠️ 需要了解AOP切面机制
|
|||
|
- ⚠️ 方法签名模式需要精心设计
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### 方案2:注解方案
|
|||
|
|
|||
|
#### 特点
|
|||
|
- **声明式**:通过注解明确声明快照行为
|
|||
|
- **可定制化**:可以针对不同方法设置不同策略
|
|||
|
- **意图明确**:代码中明确表达了快照意图
|
|||
|
|
|||
|
#### 实现方式
|
|||
|
```java
|
|||
|
@Target({ElementType.METHOD})
|
|||
|
@Retention(RetentionPolicy.RUNTIME)
|
|||
|
public @interface SnapshotAware {
|
|||
|
OperationType operation() default OperationType.QUERY;
|
|||
|
boolean createSnapshotBefore() default false;
|
|||
|
String snapshotReason() default "";
|
|||
|
}
|
|||
|
|
|||
|
@Aspect
|
|||
|
@Component
|
|||
|
public class SnapshotAspect {
|
|||
|
|
|||
|
@Around("@annotation(snapshotAware)")
|
|||
|
public Object aroundSnapshotAware(ProceedingJoinPoint joinPoint, SnapshotAware snapshotAware) {
|
|||
|
// 根据注解配置处理快照逻辑
|
|||
|
}
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
#### 业务代码(需要添加注解)
|
|||
|
```java
|
|||
|
@Service
|
|||
|
public class UserManagementService {
|
|||
|
|
|||
|
@SnapshotAware(operation = SnapshotAware.OperationType.QUERY)
|
|||
|
public User getUserById(Long id) {
|
|||
|
return userRepository.findById(id).orElse(null);
|
|||
|
}
|
|||
|
|
|||
|
@SnapshotAware(operation = SnapshotAware.OperationType.UPDATE, createSnapshotBefore = true)
|
|||
|
public User updateUser(Long id, String name, String email, String phone) {
|
|||
|
User user = userRepository.findById(id).orElse(null);
|
|||
|
if (user != null) {
|
|||
|
user.setName(name);
|
|||
|
user.setEmail(email);
|
|||
|
user.setPhone(phone);
|
|||
|
user.setUpdateTime(new Date());
|
|||
|
return userRepository.save(user);
|
|||
|
}
|
|||
|
return null;
|
|||
|
}
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
#### 优势
|
|||
|
- ✅ 意图明确,可定制化
|
|||
|
- ✅ 声明式编程,易于理解
|
|||
|
- ✅ 可以针对不同方法设置不同策略
|
|||
|
- ✅ 支持复杂的快照逻辑
|
|||
|
|
|||
|
#### 劣势
|
|||
|
- ❌ 需要手动添加注解
|
|||
|
- ❌ 容易遗漏,忘记添加注解
|
|||
|
- ❌ 代码侵入性相对较高
|
|||
|
- ❌ 维护成本较高
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### 方案3:手动集成方案
|
|||
|
|
|||
|
#### 特点
|
|||
|
- **传统方式**:在每个方法中手动添加快照逻辑
|
|||
|
- **直接控制**:可以精确控制每个方法的快照行为
|
|||
|
- **灵活性高**:可以根据具体需求定制
|
|||
|
|
|||
|
#### 实现方式
|
|||
|
```java
|
|||
|
@Service
|
|||
|
public class UserManagementService {
|
|||
|
|
|||
|
@Autowired(required = false)
|
|||
|
private SnapshotService snapshotService;
|
|||
|
|
|||
|
public User getUserById(Long id) {
|
|||
|
// 手动添加快照逻辑
|
|||
|
if (snapshotService != null) {
|
|||
|
return snapshotService.getUserWithSnapshot(id);
|
|||
|
}
|
|||
|
return userRepository.findById(id).orElse(null);
|
|||
|
}
|
|||
|
|
|||
|
public User updateUser(Long id, String name, String email, String phone) {
|
|||
|
// 手动添加快照逻辑
|
|||
|
if (snapshotService != null) {
|
|||
|
User currentUser = snapshotService.getUserWithSnapshot(id);
|
|||
|
if (currentUser != null && currentUser.getSnapshotId() != null) {
|
|||
|
snapshotService.createSnapshot(id, "用户信息更新");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 原有业务逻辑
|
|||
|
User user = userRepository.findById(id).orElse(null);
|
|||
|
if (user != null) {
|
|||
|
user.setName(name);
|
|||
|
user.setEmail(email);
|
|||
|
user.setPhone(phone);
|
|||
|
user.setUpdateTime(new Date());
|
|||
|
return userRepository.save(user);
|
|||
|
}
|
|||
|
return null;
|
|||
|
}
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
#### 优势
|
|||
|
- ✅ 直接控制,灵活性高
|
|||
|
- ✅ 可以精确控制每个方法的快照行为
|
|||
|
- ✅ 不依赖AOP机制
|
|||
|
- ✅ 代码逻辑清晰
|
|||
|
|
|||
|
#### 劣势
|
|||
|
- ❌ 代码侵入性强
|
|||
|
- ❌ 维护困难,容易出错
|
|||
|
- ❌ 扩展性差
|
|||
|
- ❌ 重复代码多
|
|||
|
- ❌ 性能开销相对较大
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
## 方案选择建议
|
|||
|
|
|||
|
### 推荐方案:方法签名模式拦截
|
|||
|
|
|||
|
**适用场景:**
|
|||
|
- 大型项目,需要快速集成快照功能
|
|||
|
- 现有代码较多,不希望大幅修改
|
|||
|
- 团队对AOP有一定了解
|
|||
|
- 需要零侵入性的解决方案
|
|||
|
|
|||
|
**理由:**
|
|||
|
1. **完全自动化**:无需修改任何业务代码
|
|||
|
2. **零侵入性**:保持代码原样,降低风险
|
|||
|
3. **智能识别**:支持多种命名规范
|
|||
|
4. **易于维护**:集中管理快照逻辑
|
|||
|
5. **性能优秀**:AOP开销很小
|
|||
|
|
|||
|
### 备选方案:注解方案
|
|||
|
|
|||
|
**适用场景:**
|
|||
|
- 需要精确控制快照行为
|
|||
|
- 团队对声明式编程有偏好
|
|||
|
- 项目规模较小,可以接受添加注解
|
|||
|
- 需要复杂的快照策略
|
|||
|
|
|||
|
**理由:**
|
|||
|
1. **意图明确**:通过注解清晰表达快照意图
|
|||
|
2. **可定制化**:可以针对不同方法设置不同策略
|
|||
|
3. **易于理解**:代码中明确表达了快照行为
|
|||
|
|
|||
|
### 不推荐:手动集成方案
|
|||
|
|
|||
|
**适用场景:**
|
|||
|
- 只有少数几个方法需要快照功能
|
|||
|
- 对AOP机制不熟悉
|
|||
|
- 需要完全控制快照逻辑
|
|||
|
|
|||
|
**理由:**
|
|||
|
1. **维护困难**:需要在每个方法中手动添加逻辑
|
|||
|
2. **容易出错**:容易遗漏或添加错误
|
|||
|
3. **扩展性差**:新增方法需要重复添加逻辑
|
|||
|
4. **代码冗余**:大量重复代码
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
## 实际应用示例
|
|||
|
|
|||
|
### 方案1:方法签名模式(当前实现)
|
|||
|
|
|||
|
```java
|
|||
|
// 业务代码完全原样
|
|||
|
@Service
|
|||
|
public class UserManagementService {
|
|||
|
|
|||
|
public User getUserById(Long id) {
|
|||
|
return userRepository.findById(id).orElse(null); // 自动被拦截
|
|||
|
}
|
|||
|
|
|||
|
public User updateUser(Long id, String name, String email, String phone) {
|
|||
|
User user = userRepository.findById(id).orElse(null);
|
|||
|
if (user != null) {
|
|||
|
user.setName(name);
|
|||
|
user.setEmail(email);
|
|||
|
user.setPhone(phone);
|
|||
|
user.setUpdateTime(new Date());
|
|||
|
return userRepository.save(user); // 自动被拦截
|
|||
|
}
|
|||
|
return null;
|
|||
|
}
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
### 方案2:注解方案
|
|||
|
|
|||
|
```java
|
|||
|
@Service
|
|||
|
public class UserManagementService {
|
|||
|
|
|||
|
@SnapshotAware(operation = SnapshotAware.OperationType.QUERY)
|
|||
|
public User getUserById(Long id) {
|
|||
|
return userRepository.findById(id).orElse(null);
|
|||
|
}
|
|||
|
|
|||
|
@SnapshotAware(operation = SnapshotAware.OperationType.UPDATE, createSnapshotBefore = true)
|
|||
|
public User updateUser(Long id, String name, String email, String phone) {
|
|||
|
User user = userRepository.findById(id).orElse(null);
|
|||
|
if (user != null) {
|
|||
|
user.setName(name);
|
|||
|
user.setEmail(email);
|
|||
|
user.setPhone(phone);
|
|||
|
user.setUpdateTime(new Date());
|
|||
|
return userRepository.save(user);
|
|||
|
}
|
|||
|
return null;
|
|||
|
}
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
### 方案3:手动集成
|
|||
|
|
|||
|
```java
|
|||
|
@Service
|
|||
|
public class UserManagementService {
|
|||
|
|
|||
|
@Autowired(required = false)
|
|||
|
private SnapshotService snapshotService;
|
|||
|
|
|||
|
public User getUserById(Long id) {
|
|||
|
if (snapshotService != null) {
|
|||
|
return snapshotService.getUserWithSnapshot(id);
|
|||
|
}
|
|||
|
return userRepository.findById(id).orElse(null);
|
|||
|
}
|
|||
|
|
|||
|
public User updateUser(Long id, String name, String email, String phone) {
|
|||
|
if (snapshotService != null) {
|
|||
|
User currentUser = snapshotService.getUserWithSnapshot(id);
|
|||
|
if (currentUser != null && currentUser.getSnapshotId() != null) {
|
|||
|
snapshotService.createSnapshot(id, "用户信息更新");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
User user = userRepository.findById(id).orElse(null);
|
|||
|
if (user != null) {
|
|||
|
user.setName(name);
|
|||
|
user.setEmail(email);
|
|||
|
user.setPhone(phone);
|
|||
|
user.setUpdateTime(new Date());
|
|||
|
return userRepository.save(user);
|
|||
|
}
|
|||
|
return null;
|
|||
|
}
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
## 总结
|
|||
|
|
|||
|
| 方案 | 侵入性 | 自动化程度 | 维护性 | 扩展性 | 性能 | 推荐度 |
|
|||
|
|------|--------|------------|--------|--------|------|--------|
|
|||
|
| 方法签名模式 | 零侵入 | 完全自动化 | 优秀 | 优秀 | 优秀 | ⭐⭐⭐⭐⭐ |
|
|||
|
| 注解方案 | 低侵入 | 半自动化 | 良好 | 良好 | 良好 | ⭐⭐⭐⭐ |
|
|||
|
| 手动集成 | 高侵入 | 手动 | 差 | 差 | 一般 | ⭐⭐ |
|
|||
|
|
|||
|
**最终推荐:方法签名模式拦截方案**
|
|||
|
|
|||
|
该方案在保持代码原样的同时,实现了完全自动化的快照功能,是最佳的解决方案。
|