公共字段自动填充

本文介绍了如何在SpringBoot项目中,通过AOP和Java反射机制,自定义注解AutoFill来自动填充业务表中的公共字段,如创建时间和修改人ID,以提高代码可维护性和减少冗余。
摘要由CSDN通过智能技术生成

1:问题分析:

业务表中的公共字段:

序号

字段名

含义

数据类型

1

create_time

创建时间

datetime

2

create_user

创建人id

bigint

3

update_time

修改时间

datetime

4

update_user

修改人id

bigint

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

2:解决思路:

序号

字段名

含义

数据类型

操作类型

1

create_time

创建时间

datetime

insert

2

create_user

创建人id

bigint

3

update_time

修改时间

datetime

insertupdate

4

update_user

修改人id

bigint

利用AOP面向切面编程

SpringBoot的AOP理解-CSDN博客

同时这里也用到了反射的思想

Java反射机制-CSDN博客

实现思路:

自定义注解 AutoFill,用于标识需要进行公共字段自动填充的方法:

编写一个注解类:
package com.sky.anno;


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;

@Retention(RetentionPolicy.RUNTIME)//描述这个注解什么时候生效
@Target(ElementType.METHOD)//当前这个注解作用在什么地方
public @interface AutoFill {

    OperationType value();
}

自定义切面类 AutoFillAspect,统一拦截加入了 AutoFill 注解的方法,通过反射为公共字段赋值

package com.sky.aop;


import com.sky.anno.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.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;

@Slf4j
@Component
@Aspect
public class AutoFillAspect {
    /**
     * 切入点
     */
    @Pointcut("@annotation(com.sky.anno.AutoFill)")
    private void pt(){}

    @Before("pt()")
    public void AutoFill(JoinPoint joinPoint) throws Throwable{
        log.info("自动填充字段");

        //1:获取到当前被拦截的数据库操作类型
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();//方法的签名对象
        System.out.println(signature);
        System.out.println(signature.getMethod());
        AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获得方法上的注解对象
        OperationType operationType = autoFill.value();//获得数据库操作类型
        //2:获取到当前被拦截操作的参数---实体
        Object[] args = joinPoint.getArgs();
        Object entity = args[0];//约定将被拦截的参数放在第一位。

        //3:准备要为这个实体赋的值
        LocalDateTime now = LocalDateTime.now();//当前时间
        Long id = BaseContext.getCurrentId();//id(通过ThreadLocal)

        //4:为这个实体赋值(当前时间,和id)(通过反射来赋值)
        Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
        Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
        if(operationType.equals(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);
                //通过反射为对象赋值
                setCreateTime.invoke(entity,now);
                setUpdateTime.invoke(entity,now);
                setCreateUser.invoke(entity,id);
                setUpdateUser.invoke(entity,id);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }else{//另一种情况就是Update
            try {
                setUpdateUser.invoke(entity, id);
                setUpdateTime.invoke(entity,now);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
注意点:
1:在这个类上加上@Aspect,表明这是一个注解了类。
2:定义这个切入点
    @Pointcut("@annotation(com.sky.anno.AutoFill)")
    private void pt(){}

    @Before("pt()")

因为这里的功能是在update和insert两个操作上,填充公共的字段,所以,可以考虑使用before这种通知类型。

3:AutoFill:
        1:获取到当前被拦截的数据库操作类型

                思考这个需求,要想获得作用在数据库操作类型

                这个AutoFill是不是已注解的方式加在上面的,所以这个问题就转化为了获取对应方法上的注解对象,

想获取对应方法上的注解对象,那得先获取对应方法的对象(反射那一块的知识),问题又转化为了获取对应方法的对象,

想获取对应方法上的对象,需要通过连接点JoinPoint获取方法的签名对象,然后来获取,这里还得提一下:如果实际拦截的是一个方法的话,可以向下转型为MethodSignature

所以具体代码:

//1:获取到当前被拦截的数据库操作类型
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();//方法的签名对象
        System.out.println(signature);
        System.out.println(signature.getMethod());
        AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获得方法上的注解对象
        OperationType operationType = autoFill.value();//获得数据库操作类型

        2:获取到当前被拦截操作的参数---实体

这个也是同理,想获得这个实体,我们可以通过JoinPoint获取参数的方法,不过我们规定这个实体对象的参数要放在第一位,因为我们后面直接调用args[0]。

        //2:获取到当前被拦截操作的参数---实体
        Object[] args = joinPoint.getArgs();
        Object entity = args[0];//约定将被拦截的参数放在第一位。

        3:准备要为这个实体赋的值

        准备这个实体赋的值比较简单,时间的话一般都是LocalDateTime.now()就行

        这个id需要用到之前的知识点:ThreadLocal

新增员工操作-CSDN博客,这一篇的最后代码优化部分有提到ThreadLocal。

        //3:准备要为这个实体赋的值
        LocalDateTime now = LocalDateTime.now();//当前时间
        Long id = BaseContext.getCurrentId();//id(通过ThreadLocal)

        4:为这个实体赋值(当前时间,和id)(通过反射来赋值)

        为这个实体赋值,就是通过放射的方法了(invoke)

//4:为这个实体赋值(当前时间,和id)(通过反射来赋值)
        Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
        Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
        if(operationType.equals(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);
                //通过反射为对象赋值
                setCreateTime.invoke(entity,now);
                setUpdateTime.invoke(entity,now);
                setCreateUser.invoke(entity,id);
                setUpdateUser.invoke(entity,id);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }else{//另一种情况就是Update
            try {
                setUpdateUser.invoke(entity, id);
                setUpdateTime.invoke(entity,now);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

在 Mapper 的方法上加入 AutoFill 注解

总结:

这一个填充公共字段虽然不是业务里的操作,不过涉及到的知识点很多:特别是SpringAOP和Java的反射。

  • 10
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值