前言
在多个业务表中都有公共字段,如create_time、create_user(insert时用到);update_time,update_user(insert和update时用到)这些。
插入数据的时候需要为这些字段赋值,会有大量重复的冗余set方法代码,后期如果表结构发生变化,代码需要跟着修改,此时就不方便修改(如果后期进行修改要重复一个个进行修改)。
实现思路:自定义注解AutoFill,用于标识需要进行公共字段自动填充的方法。然后自定义切面类AutoFillAspect,统一拦截加入了AutoFill注解的方法,通过反射为公共字段赋值。在Mapper的方法上加入AutoFill注解。
技术点:枚举,注解,AOP,反射
1.在com.sky下创建annotation包,创造一个AutoFill的Annotation注解
写入如下代码:
//自定义注解,用于标识某个方法需要进行功能字段自动填充处理
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
//数据库操作类型:UPDATE INSERT
OperationType value();
}
Target注解指定加上什么上面,Retention注解指定什么时候用
2. 在com.sky下创建aspect包,创建类AutoFillAspect,写入如下代码:
//自定义切面,实现公共字段自动填充处理逻辑
@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("开始进行公共字段自动填充...");
}
}
切入点:对哪些类的哪些方法进行拦截。@Pointcut里面写的是对哪些方法进行拦截,要满足2点:①必须是mapper下的所有类的方法,②还要有AutoFill这个注解。
通知:前置通知,后置通知,环绕通知,异常通知。
然后在sky-server下的mapper中的EmployeeMapper类里,insert上加入@AutoFill(value= OperationType.INSERT)注解,update上加入@AutoFill(value= OperationType.UPDATE)注解
3.测试:在aspect/AutoFillAspect上的log.info("开始进行公共字段自动填充...")处打上断点,然后点击小虫(断点调试),正常登录,到员工管理界面,点击修改,看是否会运行到log语句
@Before("autoFillPointCut()")
public void autoFill(JoinPoint joinPoint){
log.info("开始进行公共 字段自动填充...");
//获取到当前被拦截的方法上的数据库操作类型
MethodSignature signature = (MethodSignature) joinPoint.getSignature();//方法签名对象
AutoFill autoFill=signature.getMethod().getAnnotation(AutoFill.class);//获取方法上的注解对象
OperationType operationType =autoFill.value();//获得数据库操作类型
//获取到当前被拦截的方法的参数--实体对象
Object[] args =joinPoint.getArgs();
if (args==null || args.length==0){
return;
}
Object entity=args[0];
//准备赋值的数据
LocalDateTime now = LocalDateTime.now();
Long currentId = BaseContext.getCurrentId();
//根据当前不同的操作类型(INSERT,UPDATE),为对应的属性通过反射来赋值
if (operationType == OperationType.INSERT){
//为4个公共字段赋值
try {
Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME,LocalDateTime.class);
Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
//通过反射为对象属性赋值
setCreateTime.invoke(entity,now);
setCreateUser.invoke(entity,currentId);
setUpdateUser.invoke(entity,currentId);
setUpdateTime.invoke(entity,now);
} catch (Exception e) {
throw new RuntimeException(e);
}
} else if (operationType == OperationType.UPDATE) {
//为两个公共字段赋值
try {
Method setUpdateTime = entity.getClass().getDeclaredMethod("setUpdateTime", LocalDateTime.class);
Method setUpdateUser = entity.getClass().getDeclaredMethod("setUpdateUser", Long.class);
//通过反射为对象属性赋值
setUpdateUser.invoke(entity,currentId);
setUpdateTime.invoke(entity,now);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
然后要在mapper层的CategoryMapper和EmployeeMapper中的Insert和Update方法上加上@AutoFill注解,注解内容用OperationType.INSERT或OperationType.Update
总结
枚举,注解,AOP,反射的综合使用