实现公共字段的自动填充逻辑

需要实现的效果

公共字段自动填充

  • 问题分析
  • 实现思路
  • 代码开发
  • 功能测试

问题分析

多张业务表中的公共字段:

问题:代码冗余、不便于后期维护

实现思路

  • 自定义注解 AutoFill,用于标识需要进行公共字段自动填充的方法
  • 自定义切面类 AutoFillAspect,统一拦截加入了 AutoFill 注解的方法,通过反射为公共字段赋值
  • 在 Mapper 的方法上加入 AutoFill 注解

技术点:枚举、注解、AOP、反射

代码开发

  • 自定义注解 AutoFill

    1
    2
    3
    4
    5
    6
    7
    8
    9
    /**
    * 自定义注解,用于标识需要进行公共字段的自动填充
    */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface AutoFill {
    // 数据操作类型 UPDATE、INSERT
    OperationType value();
    }
  • 自定义切面 AutoFillAspect

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    /**
    * 自定义切面,实现公共字段的自动填充逻辑
    */
    @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("开始进行公共字段的自动填充。。。。。。");
    }

    }

  • 完善自定义切面 AutoFillAspect 的 autoFill 方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    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();

    // 根据当前不同的操作类型,为对应的属性通过反射来赋值
    if (operationType == OperationType.INSERT) {
    //为四个公共字段赋值
    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);
    setUpdateTime.invoke(entity, now);
    setUpdateUser.invoke(entity, currentId);
    } catch (Exception e) {
    throw new RuntimeException(e);
    }
    } else if (operationType == OperationType.UPDATE) {
    //为二个公共字段赋值
    try {
    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);
    } catch (Exception e) {
    throw new RuntimeException(e);
    }
    }
  • 在Mapper接口的方法上加入 AutoFill 注解

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     /**
    * 插入数据
    *
    * @param category
    */
    @Insert("insert into category(type, name, sort, status, create_time, update_time, create_user, update_user)" +
    " VALUES" +
    " (#{type}, #{name}, #{sort}, #{status}, #{createTime}, #{updateTime}, #{createUser}, #{updateUser})")
    @AutoFill(OperationType.INSERT)
    void insert(Category category);

    /**
    * 根据id修改分类
    *
    * @param category
    */
    @AutoFill(OperationType.UPDATE)
    void update(Category category);
  • 将业务层为公共字段赋值的代码注释掉

功能测试

通过观察控制台输出的SQL来确定公共字段填充是否完成