问题分析
代码冗余,且不便于后期维护。
实现思路
技术点:枚举,注解,AOP,反射
1.自定义注解AutoFill
1.在sky-server.com.sky包下建立annotation(注解)包
2.在该包下建立注解AutoFill
package com.sky.annotation;
import com.sky.enumeration.OperationType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/*
* 自定义注解,用于标识某个方法需要进行功能字段自动填充
* */
@Target(ElementType.METHOD)//表示该注解作用于方法上
@Retention(RetentionPolicy.RUNTIME) //元注解,需要在运行时去动态获取注解信息
public @interface AutoFill {
//数据库操作类型
OperationType value();//可以通过这个方法设置数据库操作类型,也可以通过这个方法取出数据库的操作类型
}
2.自定义切面类AutoFillAspect,统一拦截加入了AutoFill注解的方法,通过发射为公共字段赋值
1.在sky-server.com.sky包下建立aspect(切面)包
2.在该包下建立切面类AutoFillAspect
package com.sky.aspect;
import com.sky.annotation.AutoFill;
import com.sky.constant.AutoFillConstant;
import com.sky.context.BaseContext;
import com.sky.enumeration.OperationType;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
/*
* 自定义切面,实现公共字段自动填充处理逻辑
* */
@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("开始进行公共字段自动填充...");
//获取当前被拦截的方法上的数据库操作类型
MethodSignature signature = (MethodSignature)joinPoint.getSignature();//获得方法签名对象(默认获得的是Signnature类型的对象)
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();
//根据当前不同的操作类型,为对应的属性通过反射来赋值
if(operationType==OperationType.INSERT){
//为4个字段赋值
try {
/*利用反射获取实体对象的方法
(必须利用反射,
1是因为返回类型是Object,无法直接调用对应方法;
2是利用反射可以增强代码的复用性,不局限于一个实体类中)*/
Method setCreateTime = entity.getClass().getMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
Method setUpdateTime = entity.getClass().getMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setCreateUser = entity.getClass().getMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
Method setUpdateUser = entity.getClass().getMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
//方法.invoke(实体对象,对应方法的参数);
setCreateTime.invoke(entity,now);
setUpdateTime.invoke(entity,now);
setCreateUser.invoke(entity,currentId);
setUpdateUser.invoke(entity,currentId);
} catch (Exception e) {
throw new RuntimeException(e);
}
}else if(operationType==OperationType.UPDATE){
//为2个字段赋值
try {
//Method setUpdateTime = entity.getClass().getMethod("setUpdateTime", LocalDateTime.class);
//Method setUpdateUser = entity.getClass().getMethod("setUpdateUser", Long.class);
//为了避免方法名写错,所以单独为方法名写了一个AutoFillConstant常量类(更加的规范和优雅)
Method setUpdateTime = entity.getClass().getMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setUpdateUser = entity.getClass().getMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
setUpdateTime.invoke(entity,now);
setUpdateUser.invoke(entity,currentId);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
3.在Mapper的方法上加入AutoFill注解
1.在sky-server.com.sky.mapper包下为加入了Insert和update注解的方法再加入@AutoFfill注解。
2.将sky-server.com.sky.service.impl下使用到insert和update的方法中对字段数据的更新注释掉。(避免重复调用)