示例:通过注解实现AOP切面接口日志记录

一、定义注解

1
2
3
4
5
6
7
8
9
10
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebLog {
String moduleName() default ""; //所属模块

String methodName() default ""; //接口名称

String params() default ""; //请求参数
}
  • @Target:用于描述注解的使用范围,该注解可以使用在什么地方

    • ElementType.TYPE 应用于类、接口(包括注解类型)、枚举
    • ElementType.FIELD 应用于属性(包括枚举中的常量)
    • ElementType.METHOD 应用于方法
    • ElementType.PARAMETER 应用于方法的形参
    • ElementType.CONSTRUCTOR 应用于构造函数
    • ElementType.LOCAL_VARIABLE 应用于局部变量
    • ElementType.ANNOTATION_TYPE 应用于注解类型
    • ElementType.PACKAGE 应用于包
  • @Retention:表明该注解的生命周期

    • RetentionPolicy.SOURCE 编译时被丢弃,不包含在类文件中
    • RetentionPolicy.CLASS JVM加载时被丢弃,包含在类文件中,默认值
    • RetentionPolicy.RUNTIME 由JVM 加载,包含在类文件中,在运行时可以被获取到
  • @Documented:表明该注解标记的元素可以被Javadoc 或类似的工具文档化

二、定义切面类

2.1 切面类,处理并记录接口日志

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
@Component
@Aspect
public class WebLogSave {

@Autowired
private IAccountRedisService accountRedisService;

@Pointcut("@annotation(com.common.aop.WebLog)")
public void save() {
}

@Before("save()")
private void saveMethod(JoinPoint joinPoint) {
MethodSignature ms = (MethodSignature) joinPoint.getSignature();
WebLog annotation = ms.getMethod().getAnnotation(WebLog.class);
//普通注解参数
String modelName = annotation.getModuleName();
//spel获取表达式参数
StandardEvaluationContext ctx = SpelUtil.setContextVariables(joinPoint);
String args = SpelUtil.getElValue(annotation.getParams(), ctx);
//保存到数据库
SLog log = new SLog();
//set
webLogService.save(log);
}
}

三、spel表达式获取请求参数

3.1 定义接口

1
2
3
4
5
6
@PostMapping("/save")
@Permission(permission = "User:Add")
@WebLog(moduleName = "系统模块", methodName = "创建用户接口", params = "'用户名:' + #dto.username")
public R<Void> addUser(@RequestBody @Valid CreateUserDTO dto) {
return userService.addUser(dto);
}

3.1 spel工具类,获取方法参数

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
public class SpelUtil {
//获取表达式
public static StandardEvaluationContext setContextVariables(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
MethodSignature ms = (MethodSignature) joinPoint.getSignature();
Method targetMethod = ms.getMethod();
LocalVariableTableParameterNameDiscoverer parameterNameDiscoverer
= new LocalVariableTableParameterNameDiscoverer();
String[] parametersName = parameterNameDiscoverer.getParameterNames(targetMethod);
if (ArrayUtils.isEmpty(args) || ArrayUtils.isEmpty(parametersName) || parametersName == null) {
return new StandardEvaluationContext();
}
StandardEvaluationContext ctx = new StandardEvaluationContext();
for (int i = 0; i < args.length && i < parametersName.length; i++) {
ctx.setVariable(parametersName[i], args[i]);
}
return ctx;
}

//根据表达式获取值
public static String getElValue(String key, StandardEvaluationContext context) {
if (StringUtils.isBlank(key)) {
return "";
}
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression(key);
return exp.getValue(context, String.class);
}
}