苍穹外卖--公共字段自动填充

 问题分析

苍穹外卖--公共字段自动填充_第1张图片

代码冗余,且不便于后期维护。

 实现思路

 苍穹外卖--公共字段自动填充_第2张图片

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

 1.自定义注解AutoFill

苍穹外卖--公共字段自动填充_第3张图片

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注解的方法,通过发射为公共字段赋值

苍穹外卖--公共字段自动填充_第4张图片

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注解

 苍穹外卖--公共字段自动填充_第5张图片

 1.在sky-server.com.sky.mapper包下为加入了Insert和update注解的方法再加入@AutoFfill注解。

苍穹外卖--公共字段自动填充_第6张图片

2.将sky-server.com.sky.service.impl下使用到insert和update的方法中对字段数据的更新注释掉。(避免重复调用)

 小tips:

 1.利用反射获取实体对象的方法 (必须利用反射, 1是因为返回类型是Object,无法直接调用对应方法; 2是利用反射可以增强代码的复用性,不局限于一个实体类中)

2.为了避免方法名写错,所以单独为方法名写了一个AutoFillConstant常量类(这样可以使得代码更加的规范和优雅)

3.在获取当前被拦截的方法的参数时,会默认获取的第一个参数为所求实体(entity)对象。

你可能感兴趣的:(java,java,服务器,spring,boot)