使用AOP切面实现日志记录功能

基于注解的方式,使用AOP切面实现日志记录功能

1. 什么是AOP

AOP称为面向切面编程,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为-可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。

常见的AOP使用场景:

  • 记录操作日志
  • 缓存处理
  • Spring中内置的事务处理

2.AOP实现日志记录

2.1.导入依赖

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

2.2.创建一个注解作为切点

新建一个注解,内部可以自定义一些属性

1
2
3
4
5
6
7
8
9
10
11
12
import java.lang.annotation.*;

//TYPE代表可以放在类上面 //METHOD代表可以放在方法上
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogAnnotation {

//操作
String operation() default "";

}

2.3.创建切面类

切面类中会定义通知和切点的关系

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
import com.me.bibackend.annotation.LogAnnotation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import java.lang.reflect.Method;

@Component
@Aspect
public class LogAspect {

//定义一个切点,这里是表示所有使用了LogAnnotation注解的方法
@Pointcut("@annotation(com.me.bibackend.annotation.LogAnnotation)")
public void pt() {}

//环绕通知 标记切点为pt()
//也就是所有被@LogAnnotation标记的方法都会环绕通知
@Around("pt()")
public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
StopWatch stopWatch = new StopWatch();
//开启计时
stopWatch.start();
//执行方法
Object result = joinPoint.proceed();
//结束计时
stopWatch.stop();
//以下可以写日志相关操作
System.out.println("方法耗时--"+stopWatch.getTotalTimeMillis()+"ms");
handleLog(joinPoint,result);
//返回结果
return result;
}

private void handleLog(ProceedingJoinPoint joinPoint, Object result) {
// 获取注解内容
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);
String operation = logAnnotation.operation();
//获取方法名
String methodName = joinPoint.getSignature().getName();
//获取类名
String className = joinPoint.getTarget().getClass().getName();
//获取参数
Object[] args = joinPoint.getArgs();
//获取返回值
String resultStr = result.toString();
//打印日志
System.out.println("方法名:"+methodName
+",类名:"+className
+",参数:"+args
+",返回值:"+resultStr
+",操作:"+operation);
}

}

2.4.测试

1
2
3
4
5
6
7
8
9
10
@RestController
@RequestMapping("/test")
public class TestController {

@GetMapping("/log")
@LogAnnotation(operation = "测试")
public String testLog() {
return "test log";
}
}

结果如下: