概要
在已实现日志插入功能的基础上,使用AOP优化代码
使用方式
// 在controller层的方法上填写注解,下面则是我在编辑用户接口上写入的例子,#{#xxx}都会被替换为xxx对应参数值。
@OperationLogAnnotation(actionModule = "user", actionType = "编辑", actionUrl = "/admins/users/#{#uid}", actionContent = "#{#users}")
@PutMapping("/users/{uid}")
public JsonResult update(@RequestBody @Validated UsersUpdateParam users, @PathVariable Integer uid) {...}
/* 例如 发起请求中,uid为1,users为{phone:12345}的对象
日志则会保存为 {actionModule:"user",
actionType:"编辑",
actionUrl:"/admins/users/1",
actionContent:"{phone:12345}"}
*/
导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.58</version>
</dependency>
定义日志注解
package com.xyongfeng.aop;
import java.lang.annotation.*;
@Target(ElementType.METHOD)// 注解放置的目标位置即方法级别
@Retention(RetentionPolicy.RUNTIME)// 注解在哪个阶段执行
@Documented
public @interface OperationLogAnnotation {
String actionModule(); // 操作模块
String actionType(); // 操作类型
String actionUrl(); // 操作路径
String actionContent() default ""; // 操作内容
}
编写切面类
package com.xyongfeng.aop;
import com.alibaba.fastjson.JSONObject;
import com.xyongfeng.pojo.JsonResult;
import com.xyongfeng.service.AdminLogService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.Expression;
import org.springframework.expression.ParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Aspect
@Component
public class OperationLogAspect {
/**
* 日志业务类
*/
@Autowired
private AdminLogService adminLogService;
/**
* 设置操作日志切入点,在注解的位置切入代码
*/
@Pointcut("@annotation(com.xyongfeng.aop.OperationLogAnnotation)")
public void operLogPoinCut() {
}
@AfterReturning(returning /**
* 记录操作日志
* @param joinPoint 方法的执行点
* @param result 方法返回值
* @throws Throwable
*/ = "result", value = "operLogPoinCut()")
public void saveOperLog(JoinPoint joinPoint, JsonResult result) {
// 从切面织入点处通过反射机制获取织入点处的方法
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
//获取切入点所在的方法
Method method = signature.getMethod();
//获取注解信息
OperationLogAnnotation annotation = method.getAnnotation(OperationLogAnnotation.class);
if (annotation != null) {
//获取参数值
Object[] parameterArgs = joinPoint.getArgs();
//获取参数值类型
// Class<?>[] parameterTypes = method.getParameterTypes();
//获取参数名
String[] parameterNames = new DefaultParameterNameDiscoverer().getParameterNames(method);
//建立上下文字典,储存参数信息
StandardEvaluationContext context = new StandardEvaluationContext();
for (int i = 0; i < parameterArgs.length; i++) {
if (parameterNames[i] != null){
// JSONObject.toJSONString 可以将对象转换为json字符串
context.setVariable(parameterNames[i], JSONObject.toJSONString(parameterArgs[i]));
}
}
// 使用SpEL表达式替换文本
SpelExpressionParser parser = new SpelExpressionParser();
// 替换url
Expression urlExpression = parser.parseExpression(annotation.actionUrl(), ParserContext.TEMPLATE_EXPRESSION);
// 替换content
Expression contentExpression = parser.parseExpression(annotation.actionContent(), ParserContext.TEMPLATE_EXPRESSION);
// 读取注解信息,执行替换,再进行日志插入
adminLogService.insert(
annotation.actionModule(),
annotation.actionType(),
(String) urlExpression.getValue(context),
(String) contentExpression.getValue(context),
result.getCode().equals(200));
// result.getCode().equals(200) 则是返回操作结果是否成功
}
}
}
参考链接
SpringBoot+AOP实现用户操作日志的记录: https://blog.csdn.net/qq_42570879/article/details/108830089
Java注解动态解析获取方法参数值: https://blog.csdn.net/qq_21480147/article/details/120063385
Spring表达式实现变量替换: https://blog.csdn.net/zhigang0529/article/details/83178167