公共字段填充

Lu Lv3

回顾AOP切面编程

获取方法名、参数值、参数值类型、目标注解对象、目标方法所在类、返回值类型

1. 引入Maven依赖

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2. 创建一个自定义注解类CacheableTest

1
2
3
4
5
6
7
8
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface CacheableTest {
String key();
String value() default "";
int expireTime() default 3600;
}

3. 创建一个controller类,并加入该方法

1
2
3
4
5
6
@RequestMapping("/findPage2")
@CacheableTest(key="haha",value = "hehe")
public String findPage2(Integer pageNumber, Double pageSize){
System.out.println("findPage2请求成功!!!");
return "请求成功";
}

4. 创建一个切面类 TestAOP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@Component
@Aspect
public class TestAop {


@Pointcut("execution(public * ink.lusy.helloController.*(..))") //第一个星号指返回值类型为任意
private void pointCut(){};


@Before(value = "pointCut()")
public void logBefore(JoinPoint joinpoint) {
System.out.println("----------Before开始-----------");

System.out.println("方法名:"+ joinpoint.getSignature().getName());
System.out.println("参数值集合:"+ Arrays.asList(joinpoint.getArgs()));
System.out.println("参数值1类型:"+ joinpoint.getArgs()[0].getClass().getTypeName());
System.out.println("参数值2类型:"+ joinpoint.getArgs()[1].getClass().getTypeName());

String classType = joinpoint.getTarget().getClass().getName();
System.out.println("获取目标方法所在类:"+ classType);
//获取目标注解对象,CacheableTest是自定义的一个注解
MethodSignature methodSignature = (MethodSignature) joinpoint.getSignature();
CacheableTest cacheable = methodSignature.getMethod().getAnnotation(CacheableTest.class);
Class returnType = methodSignature.getReturnType();
System.out.println("目标注解对象:"+ cacheable);
System.out.println("返回值类型 = " + returnType);
System.out.println("----------Before结束-----------");


}

@After(value = "pointCut()")
public void logAfter(JoinPoint joinpoint) {
System.out.println("---------After开始------------");

System.out.println("---------After结束-------------");
}

}

执行结果:

image-20241109222954073

在请求时pageSize写的是1,这里自动转成1.0

具体实现

问题复现

在添加、修改 管理员、用户、菜品 等数据时,总是要重复的修改相同的字段:CreateTimeUpdateTimeCreateUserUpdateUser

解决方案

使用AOP切面编程在施行对应的新增、修改 SQL语句之前,给对应字段做出统一的处理

具体流程

  1. 引入Maven依赖
1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
  1. 创建一个枚举类OperationType:区分新增、更新操作
1
2
3
4
5
6
public enum OperationType {
//更新操作
UPDATE,
//插入操作
INSERT
}
  1. 创建一个自定义注解类AutoFill:自定义注解,用于标识某个方法需要进行功能字段自动填充处理
1
2
3
4
5
6
7
8
9
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)

public @interface AutoFill {
// 数据库操作类型
OperationType value();

}
  1. 创建一个切面类 AutoFillAspect:自定义切面,实现公共字段自动填充处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
@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) {
// 可先进行调试,是否能进入该方法,提前在mapper方法添加@AutoFill注解
log.info("开始进行公共字段的赋值");
MethodSignature signature = (MethodSignature) joinPoint.getSignature();//方法签名对象
AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获取方法上的注解对象
OperationType operationType = autoFill.value();//获取数据库操作类型

//获取到当前被拦截的方法的参数--实体对象
Object[] args = joinPoint.getArgs();
if (args <mark> null || args.length </mark> 0) {
return;
}

Object entity = args[0]; // 约定好要使用公共字段填充,就必须把操作的对象放在第一个参数

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

//根据当前不同的操作类型,为对应的属性通过反射来赋值
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 (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}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();
}
}
System.out.println(entity);
}
}
  1. 使用示例:
1
2
3
4
5
@Insert("insert into employee (name, username, password, phone, sex, id_number, create_time, update_time, create_user, update_user) " +
"VALUES " +
"(#{name},#{username}, #{password}, #{phone}, #{sex}, #{idNumber}, #{createTime}, #{updateTime}, #{createUser}, #{updateUser})")
@AutoFill(value = OperationType.INSERT)
void insert(Employee employee);
  • Title: 公共字段填充
  • Author: Lu
  • Created at : 2024-07-17 02:14:57
  • Updated at : 2024-07-17 04:14:07
  • Link: https://lusy.ink/2024/07/17/公共字段填充/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments