1. 公共字段自动填充技术解析
1.1 问题背景与解决方案选型
在典型的业务系统开发中,我们经常会遇到多个数据表包含相同字段的情况。以餐饮管理系统为例,员工表(employee)、菜品表(dish)、分类表(category)等都可能包含以下四个公共字段:
- create_time(创建时间)
- create_user(创建人)
- update_time(更新时间)
- update_user(更新人)
传统开发方式中,我们会在每个Mapper的insert和update方法中手动设置这些字段值。这种方式存在三个明显问题:
- 代码冗余:相同的赋值逻辑在多个地方重复出现
- 维护困难:当字段需要调整时,需要修改所有相关方法
- 容易遗漏:开发人员可能忘记为某些方法添加字段设置
Spring AOP(面向切面编程)为解决这类问题提供了优雅的方案。其核心思想是将这些横切关注点(Cross-Cutting Concerns)从业务逻辑中分离出来,通过切面统一处理。这种方案相比其他方案(如基类继承、MyBatis拦截器等)具有以下优势:
- 非侵入性:不需要修改原有业务代码
- 灵活性:可以精确控制需要拦截的方法
- 可维护性:修改逻辑只需调整切面类
1.2 核心实现细节
1.2.1 自定义注解设计
我们首先定义@AutoFill注解来标记需要自动填充的方法:
java复制@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
OperationType value(); // 标识操作类型(INSERT/UPDATE)
}
这里使用枚举来定义操作类型:
java复制public enum OperationType {
INSERT,
UPDATE
}
提示:将操作类型定义为枚举而非字符串,可以在编译期就发现类型错误,避免运行时问题。
1.2.2 切面类实现
切面类的核心任务是:
- 拦截带有
@AutoFill注解的方法 - 根据操作类型填充相应字段
- 通过反射设置字段值
完整实现如下:
java复制@Aspect
@Component
@Slf4j
public class AutoFillAspect {
@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
public void autoFillPointcut(){}
@Before("autoFillPointcut()")
public void autoFill(JoinPoint joinPoint) {
log.info("开始公共字段自动填充...");
// 1. 获取操作类型
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);
OperationType operationType = autoFill.value();
// 2. 获取实体对象
Object[] args = joinPoint.getArgs();
if(args == null || args.length == 0) return;
Object entity = args[0];
// 3. 准备数据
LocalDateTime now = LocalDateTime.now();
Long currentUserId = BaseContext.getCurrentId();
// 4. 根据操作类型反射赋值
try {
if(operationType == OperationType.INSERT) {
Method setCreateTime = entity.getClass().getDeclaredMethod("setCreateTime", LocalDateTime.class);
Method setCreateUser = entity.getClass().getDeclaredMethod("setCreateUser", Long.class);
setCreateTime.invoke(entity, now);
setCreateUser.invoke(entity, currentUserId);
}
