AOP切面实现数据库公共字段的填充

数据库中多个表有同样的字段,比如创建人,创建时间,更新人,更新时间,这四个属性在数据库表的设计中重复出现,且每个模块都执行同一操作进行处理,并无不同。也就是在每个表中在进行新增或者修改操作时,都需要写一遍相同的代码进行赋值,代码就稍显冗余,我们可以使用AOP切面编程,在对mapper的赋值时织入(类似拦截器),统一进行处理。

首先我们要思考当满足什么条件,我们就织入。也就是当是mapper文件下的insert或者update方法时我们进行操作,用来区分方法最好的方法就是加入自定义注解。

加入自定义注解

以下为固定写法,自定义注解中的是注解属性,如下图中value是我们自己写的注解属性的名称,它的返回值类型是OperationType(enum),OperationType是个枚举类,包括insert和update两个属性。这样, 在使用注解时 ,  给注解属性赋值insert或者update,通过获取注解属性的值,就能区分insert操作和update操作。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {

//数据库操作类型
    OperationType value();
}

AOP切面进行统一处理

分为以下两步

1.选择切点建立连接点

切点即为@Pointcut(),execution选择所有mapper类下的所有方法,annotation用于指定注解。连接点为autoFillPointCut方法

2.在连接点上执行动作(通知)

分为前置通知@before(在目标方法执行之前执行,用于设置前置条件、参数验证或者执行前的日志记录)和后置通知@after(在目标方法执行完毕后执行,无论目标方法是否成功完成。通常,这里可以进行资源清理或者后续处理)

在这里我们肯定选择@before,书写内部逻辑即可。

  1. 获取方法上的注解对象,用于判断是insert操作还是update操作
  2. 获取被拦截的实体对象(需要被赋值的对象)
  3. 准备赋值需要的数据(创建/更新时间都取当前时间,创建/更新人都取执行本次操作的id)
  4. 根据不同操作类型,通过反射对实体对象进行赋值(通过反射获取set方法,使用set方法进行赋值)
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 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.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.LocalDateTime;

/**
 * 自定义切面
 */
@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){
        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){
            //为4个公共字段赋值
            try {
                //通过反射得到set方法
                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){
                e.printStackTrace();
            }
        }else if(operationType==OperationType.UPDATE){
            //为2个公共字段赋值
            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){
                e.printStackTrace();
            }
        }
    }
}

以上AutoFillConstant为一个常量类,存储的是方法名,使程序得到进一步封装。

反射知识补充:通过对象.getClass()得到一个对象类的class对象,调用getDeclaredMethod(方法名,参数类型)获取方法,通过invoke(目标对象,参数)调用方法,即可完成将参数赋值给目标对象的操作

优雅,永不过时!
  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值