【开发心得】基于切面实现事件记录
环境:
Spring boot 2.x
依赖:
直接引入AOP starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
或者单独引入以下两个依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
apache-common-lang3依赖
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
关于启动类添加@EnableAspectJAutoProxy注解。如果直接引入AOP starter 默认可以不手动开启(约定配置里默认开启了),当然可以手动使用该注解,或者在application.yml使用配置方式开启。
1.如果需要半侵入方式记录数据。则需要自定义注解。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EventTracking {
String tableName(); // 表名
DbEventType type(); // 类型
String relatedId(); // id
}
2.切面类
切点:
2.1 基于注解的切点
@Pointcut("@annotation(com.xxxx.EventTracking)")
public void beforePointCut() {
}
2.2 基于切点表达式的切点 (切点表达式很多写法,可以使用通配符模糊匹配)
@Pointcut("execution(* com.xxxx.update(com.xxxx.xxTask))")
public void beforexxxUpdate() {
}
切面:(多个表达式之间使用|| && 以及 or and 等符号连接)
@Before("beforexxxxUpdate() || beforeFaceUpdate()")
public void getTrackingInfoBeforeByExecution(JoinPoint joinPoint){}
珠玑:
获取注解值与传参参数。首先要知道MethodSignature的getParameterNames能够拿到全部的参数名称。然后JoinPoint可以获取到Args参数数组。这里使用的ArraysUtils.indexOf是apache lang3的方法,请自行引入。
// 获取埋点所需参数
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
EventTracking annotation = signature.getMethod().getAnnotation(EventTracking.class);
String tableName = annotation.tableName();
DbEventType type = annotation.type();
String relatedData = annotation.relatedData();
// 参数名数组
String[] parameters = signature.getParameterNames();
// 参数值
Object[] args = joinPoint.getArgs();
Long rId = null;
int index = ArrayUtils.indexOf(parameters, relatedData);
if (index != -1) {
Object obj = args[index];
if (obj == null) {
return;
}
JSONObject jsonObject = (JSONObject) JSONObject.toJSON(obj);
String idString = jsonObject.getString("id");
if (StringUtils.isBlank(idString)) {
return;
}
rId = Long.valueOf(String.valueOf(idString)).longValue();
eventTrackingService.buildEventTrackingData(tableName, rId, type);
}
3. 当然可以根据参数 直接不使用注解的方式进行实现,无非需要根据函数名或者完全遍历参数的情况下进行获取。
关于AOP的基础知识这里不做分享, 关于 @Around @Before @After等切面方式也请自行学习。
本文主要分享的是一个基于Springboot2.x的AOP实现以及在AOP使用过程中的小技巧。