1. 需求
新增、修改了员工或者菜品或者其他什么什么都需要记录修改时间和修改人的id,每个方法都需要获取当前时间、修改人id,再给实体类赋值,重复代码很多,能否批量处理
2. 思路
1.用自定义注解表示新增、修改方法
2.AOP获取切入点
3.前置通知,获取修改时间等字段
4.反射赋值
3. 解决方案
3.1 自定义注解
//自定义注解,用于标识方法需要进行公共字段填充
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
//只需要考虑update和insert
OperationType value();
}
OperationType是一个自定义的枚举,包括INSERT和UPDATE,方便区分。
因为在本例中,insert操作需要更新:新增时间、新增人、修改时间、修改人 四个字段;update操作需要更新:修改时间、修改人 两个字段
OperationType定义如下:
public enum OperationType {
/**
* 更新操作
*/
UPDATE,
/**
* 插入操作
*/
INSERT
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
这两行注解记着就行,程式化的
3.2 使用自定义注解
3.3 切入点表达式,以及通知部分
//自定义切面,实现公共字段的自动填充
@Aspect
@Component
@Slf4j
public class AutoFillAspect {
//切入点,对哪些类的哪些方法进行拦截
//mapper的所有类所有方法中加入了自定义注解的方法
@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill))")
public void autoFillPointCut(){}
//通知
@Before("autoFillPointCut()")//前置通知,进行功能增强,即公共字段赋值
public void autoFill(JoinPoint joinPoint) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {//连接点作为参数传入,通过连接点可以获取当前被拦截的方法、方法参数
log.info("开始进行公共字段填充");
//判断拦截的是update还是insert操作
MethodSignature signature = (MethodSignature) joinPoint.getSignature(); //获取方法签名对象
AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获取方法上的注解对象
OperationType operationType = autoFill.value(); //获得操作类型(是个枚举)
//获取当前被拦截方法的参数--实体对象,约定:给mapper传参时,实体对象放在第一个参数
Object[] args = joinPoint.getArgs();
if(args==null || args.length==0){
return;
}
Object entity = args[0];
//准备需要赋值的数据
LocalDateTime now = LocalDateTime.now();
Long currentId = BaseContext.getCurrentId();
//根据不同操作类型,通过反射进行赋值
if(operationType==OperationType.INSERT){
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);
setUpdateTime.invoke(entity, now);
setUpdateUser.invoke(entity, currentId);
}else{
Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME,LocalDateTime.class);
Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
setUpdateTime.invoke(entity, now);
setUpdateUser.invoke(entity, currentId);
}
}
}
几点说明:
1.切入点表达式,匹配了mapper包下的所有类的所有带AutoFill注解的方法
2.判断是insert还是update方法,分以下几步:
1)joinPoint.getSignature()获取方法签名对象
2)signature.getMethod().getAnnotation(AutoFill.class)获取方法上的注解对象
3)autoFill.value()获得枚举操作类型
4)if判断即可
3.怎么拿到传参的实体类对象?约定实体类对象作为第一个参数,获取参数数组后,取第一个
4.@Before(“autoFillPointCut()”)使用前置通知,在执行前进行增强(即公共字段赋值)
5.反射赋值两步走:
1).getClass().getDeclaredMethod获得Method对象
2).invoke调用set方法赋值
至此,就可以把service层手动赋值的代码删除啦!