公共字段的自动填充


公共字段的填充

采用AOP增强实现公共字段的填充

1、背景

通用字段,无法统一管理,在后期维护的时候就很麻烦。

AOP

2、前置准备

  1. 枚举类
package com.sky.enumeration;

/**
 * 数据库操作类型
 */
public enum OperationType {

    /**
     * 更新操作
     */
    UPDATE,

    /**
     * 插入操作
     */
    INSERT
}
  1. 公共字段自动填充常量
package com.sky.constant;

/**
 * 公共字段自动填充相关常量
 */
public class AutoFillConstant {
    /**
     * 实体类中的方法名称
     */
    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";
}
  1. ThreadLocal
package com.sky.context;

/**
 * BaseContext类提供了线程安全的上下文管理,主要用于在跨不同操作或服务调用的环境中
 * 传递和存取特定于线程的数据。这对于需要在不同操作之间保持数据隔离的场合特别有用,
 * 比如在多用户环境中保持用户身份信息。
 */
public class BaseContext {

    // 使用ThreadLocal来存储每个线程独有的数据副本,避免数据共享带来的线程安全问题
    public static ThreadLocal<Long> threadLocal = new ThreadLocal<>();

    /**
     * 设置当前线程的ID。
     * 这个ID可以是任何需要在线程之间保持和传递的标识信息,如用户ID、事务ID等。
     *
     * @param id 需要设置的当前线程的ID。
     */
    public static void setCurrentId(Long id) {
        threadLocal.set(id);
    }

    /**
     * 获取当前线程的ID。
     * 这个方法用于获取之前通过setCurrentId方法设置的ID,以便在后续的操作中使用。
     *
     * @return 当前线程的ID,如果之前没有设置,则返回null。
     */
    public static Long getCurrentId() {
        return threadLocal.get();
    }

    /**
     * 移除当前线程的ID。
     * 这个方法用于清理线程的ID信息,以释放资源,特别是在线程可能被重用的环境中。
     */
    public static void removeCurrentId() {
        threadLocal.remove();
    }
}

3、自定义注解

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 {
    //    指定当前数据库操作数据库的类型(update insert),通过枚举的方式来指定
    OperationType value();
}

4、自定义切面类

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.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;

/**
 * 自定义切面类,实现公共字段自动填充的逻辑,统一拦截加入了AutoFill注解的方法。
 */
@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("开始进行公共字段的填充: ");
//1.获取当前被拦截的方法上的数据库操作类型
        MethodSignature signature = (MethodSignature) joinPoint.getSignature(); // 方法签名对象
        AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class); // 获取方法的注解对象
        OperationType operationType = autoFill.value(); //获得数据库的操作类型

//2.获取当前被拦截的方法的参数--实体数据
        Object[] args = joinPoint.getArgs();
        if (args == null || args.length == 0) return;
        Object entity = args[0]; // 默认选取mapper类中被注解的方法,第一个实体作为参数

//3.准备赋值的数据
        LocalDateTime now = LocalDateTime.now();
        Long currentId = BaseContext.getCurrentId();

//4.根据不同的操作类型,为对应的属性通过反射来赋值
        if (operationType == OperationType.INSERT) {
//            为4个公共字段进行赋值
            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) {
                e.printStackTrace();
            }

        } else if (operationType == OperationType.UPDATE) {
//            为2个公共字段进行赋值
            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);
                setCreateUser.invoke(entity, currentId);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

5、应用

/**
 * 插入数据
 * @param category
 * 使用AutoFill注解来自动填充操作类型
 * 这个注解用于在执行特定操作时自动填充字段
 * 当操作类型为INSERT时,会自动填充标记了这个注解的字段
 */
@AutoFill(value = OperationType.INSERT)
@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})")
void insert(Category category);

6、serviceImpl中重复的操作注释掉

    /**
     * 编辑员工信息
     *
     * @param employeeDTO
     * @return
     */
    @Override
    public void update(EmployeeDTO employeeDTO) {
        Employee employee = new Employee();
        BeanUtils.copyProperties(employeeDTO, employee);
//        employee.setUpdateTime(LocalDateTime.now());
//        employee.setUpdateUser(BaseContext.getCurrentId());
        employeeMapper.update(employee);
    }

项目文件目录

├─.idea
│ └─dataSources
│ └─11294c90-6f5c-45cc-85db-56a489e6218d
│ └─storage_v2
│ └─src
│ └─schema
├─sky-common
│ ├─src
│ │ ├─main
│ │ │ ├─java
│ │ │ │ └─com
│ │ │ │ └─sky
│ │ │ │ ├─constant
│ │ │ │ ├─context
│ │ │ │ ├─enumeration
│ │ │ │ ├─exception
│ │ │ │ ├─json
│ │ │ │ ├─properties
│ │ │ │ ├─result
│ │ │ │ └─utils
│ │ │ └─resources
│ │ └─test
│ │ └─java
│ └─target
│ ├─classes
│ │ ├─com
│ │ │ └─sky
│ │ │ ├─constant
│ │ │ ├─context
│ │ │ ├─enumeration
│ │ │ ├─exception
│ │ │ ├─json
│ │ │ ├─properties
│ │ │ ├─result
│ │ │ └─utils
│ │ └─META-INF
│ ├─generated-sources
│ │ └─annotations
│ ├─generated-test-sources
│ │ └─test-annotations
│ ├─maven-status
│ │ └─maven-compiler-plugin
│ │ ├─compile
│ │ │ └─default-compile
│ │ └─testCompile
│ │ └─default-testCompile
│ └─test-classes
├─sky-pojo
│ ├─src
│ │ ├─main
│ │ │ ├─java
│ │ │ │ └─com
│ │ │ │ └─sky
│ │ │ │ ├─dto
│ │ │ │ ├─entity
│ │ │ │ └─vo
│ │ │ └─resources
│ │ └─test
│ │ └─java
│ └─target
│ ├─classes
│ │ └─com
│ │ └─sky
│ │ ├─dto
│ │ ├─entity
│ │ └─vo
│ ├─generated-sources
│ │ └─annotations
│ ├─generated-test-sources
│ │ └─test-annotations
│ ├─maven-archiver
│ ├─maven-status
│ │ └─maven-compiler-plugin
│ │ ├─compile
│ │ │ └─default-compile
│ │ └─testCompile
│ │ └─default-testCompile
│ └─test-classes
└─sky-server
├─src
│ ├─main
│ │ ├─java
│ │ │ └─com
│ │ │ └─sky
│ │ │ ├─annotation
│ │ │ ├─aspect
│ │ │ ├─config
│ │ │ ├─controller
│ │ │ │ ├─admin
│ │ │ │ └─user
│ │ │ ├─handler
│ │ │ ├─interceptor
│ │ │ ├─mapper
│ │ │ └─service
│ │ │ └─impl
│ │ └─resources
│ │ └─mapper
│ └─test
│ └─java
└─target
├─classes
│ ├─com
│ │ └─sky
│ │ ├─annotation
│ │ ├─aspect
│ │ ├─config
│ │ ├─controller
│ │ │ └─admin
│ │ ├─handler
│ │ ├─interceptor
│ │ ├─mapper
│ │ └─service
│ │ └─impl
│ └─mapper
├─generated-sources
│ └─annotations
├─generated-test-sources
│ └─test-annotations
├─maven-status
│ └─maven-compiler-plugin
│ ├─compile
│ │ └─default-compile
│ └─testCompile
│ └─default-testCompile
└─test-classes


文章作者: ZhangYao
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 ZhangYao !
  目录