公共字段的填充
采用AOP增强实现公共字段的填充
1、背景
通用字段,无法统一管理,在后期维护的时候就很麻烦。
2、前置准备
- 枚举类
package com.sky.enumeration;
/**
* 数据库操作类型
*/
public enum OperationType {
/**
* 更新操作
*/
UPDATE,
/**
* 插入操作
*/
INSERT
}
- 公共字段自动填充常量
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";
}
- 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