出现的问题:
编写修改或添加功能时,都要在SQL语句中给修改时间、修改ID等公共字段进行赋值,显得很笨重、不灵活。从懒人角度来讲是没有必要的,浪费不必要的功。那我们可以如何解决?
解决方法:
我们可以使用动态代理的方法,对原有方法进行加强,在需要加强的方法上做个标记既可
运用技术:AOP
思路:
1、自定义注释
2、在需要加强的方法上添加该注解
3、实现自定义切面类,通过反射获取方法的注释,若有我们自定义的注释则进行加强
步骤:
0、准备阶段(根据个人情况而定)
/**
* 公共字段自动填充相关常量
*/
public class AutoFillConstant {
/**
* 操作类型
*/
public static final String INSERT = "insert";
public static final String UPDATE = "update";
/**
* 实体类中的方法名称
*/
public static final String SET_CREATE_TIME = "setCreateTime";
public static final String SET_UPDATE_TIME = "setUpdateTime";
public static final String SET_CREATE_USER = "setCreateUser";
public static final String SET_UPDATE_USER = "setUpdateUser";
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
//。。。省略其他属性
private LocalDateTime createTime;
private LocalDateTime updateTime;
private Long createUser;
private Long updateUser;
}
1、自定义注释
/**
* 自动填充
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
//操作的类型:insert update
String type();
}
2、实现相关逻辑的切面类
/**
* 自定义切面类,统一为公共字段赋值
*/
@Aspect
@Component
@Slf4j
public class AutoFillAspect {
//切面 = 通知 + 切入点
/**
* 切入点
*/
@Pointcut("execution(* com.reggie.mapper.*.*(..)) && @annotation(com.reggie.annotation.AutoFill)")
public void autoFillPointCut() {
}
/**
* 通知 自动填充公共字段
*
* @param joinPoint
*/
@Before("autoFillPointCut()")
public void AutoFillAdvice(JoinPoint joinPoint) {
log.info("公共字段自动填充...");
//获得方法签名对象
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
//获得方法上的注解
AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);
//获得注解中type的值
String type = autoFill.type(); // insert update
//获取当前目标方法的参数
Object[] args = joinPoint.getArgs();
//数值满足下面条件时,说明没有参数直接结束
if (args == null || args.length == 0) {
return;
}
//实体对象
Object entity = args[0];
//准备赋值的数据
LocalDateTime time = LocalDateTime.now();
Long empId = BaseContext.getCurrentId();
if (type.equals(AutoFillConstant.INSERT)) {
//当前执行的是insert操作,为4个字段赋值
try {
//获得set方法对象----Method
Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
//通过反射调用目标对象的方法
setCreateTime.invoke(entity, time);
setUpdateTime.invoke(entity, time);
setCreateUser.invoke(entity, empId);
setUpdateUser.invoke(entity, empId);
} catch (Exception ex) {
log.error("公共字段自动填充失败:{}", ex.getMessage());
}
} else {
//当前执行的是update操作,为2个字段赋值
try {
//获得set方法对象----Method
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, time);
setUpdateUser.invoke(entity, empId);
} catch (Exception ex) {
log.error("公共字段自动填充失败:{}", ex.getMessage());
}
}
}
}
3、在需要加强的方法上使用自定义注释即可
如:
/** * 插入数据 * @param employee */ @AutoFill(type = AutoFillConstant.INSERT) void insert(Employee employee);
/** * 根据id修改员工信息 * @param employee */ @AutoFill(type = AutoFillConstant.UPDATE) void update(Employee employee);