通过AOP 实现打印全局日志

网友投稿 602 2022-05-30

常用注解

1、@Before

修饰一个方法时,该方法将作为Before增强处理

使用@Before修饰事,需要指定一个value属性值,该属性值指定一个切入点表达式(既可以是一个已有的切入点,也可以直接定义切入点表达式),用于指定该增强处理将被织入哪些切入点

表示在切入点执行前需要进行的操作或者需要执行的方法

2、@After

同Before

表示在切入点执行后,进行哪些操作

通常用于资源释放

3、 @Around

Around增强处理是功能比较强大的增强处理;

近似等于Before增强处理和AfterReturning增强处理的总和

既可以在执行目标方法之前织入增强动作,也可以在执行目标方法之后织入增强动作

Around增强处理可以改变执行目标方法的参数值,也可以改变执行目标方法之后的返回值

Around增强处理虽然功能强大,但通常需要在线程安全的环境下使用,所以一般用Before和AfterReturning增强处理能解决的问题,不建议用Around

如果需要目标方法执行之前和之后共享某种状态数据,则应该考虑使用Around增强处理;尤其是需要改变目标方法的返回值时,则只能使用Around增强处理了

@Around增强处理事,需要指定一个value属性,该属性指定该增强处理被植入的切入点

当定义一个Around增强处理方法时,该方法的第一个形参必须是ProceedingJoinPoint类型(至少包含一个形参),在增强处理方法体内,调用ProceedingJoinPoint参数的proceed()方法才会执行目标方法——这就是Around增强处理可以完全控制目标方法的执行时机、如何执行的关键;如果程序没有调用ProceedingJoinPoint参数的proceed()方法,则目标方法不会被执行。

调用ProceedingJoinPoint参数的proceed()方法时,还可以传入一个Object[]对象作为参数,该数组中的值将被传入目标方法作为执行方法的实参

代码实现

maven依赖

//打印日志的依赖,lombok里还包含了实体注解data org.projectlombok lombok test //aop依赖 org.aspectj aspectjweaver 1.9.6 //引入lombok的日志依赖 org.projectlombok lombok 1.18.12 com.google.code.gson gson 2.8.6

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

aop

学习了Springboot的都知道,SpringBoot默认配置了AOP。这也是SpringBoot的好处,很多默认配置都给我们简化了。

package com.example.springbootorderrabbitmqproducer.AOP; import com.google.gson.Gson; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; /** * @author 康世行 * @Title: * @Package com.example.springbootorderrabbitmqproducer.AOP * @Description: 全局打印日志 * @date 2021-12-09 8:05 */ @Aspect @Component @Slf4j public class Logs { /** 以 controller 包下定义的所有请求为切入点 */ @Pointcut("execution(public * com.example.springbootorderrabbitmqproducer.controller..*.*(..))") public void webLog() {} /** * 在切点之前织入 * @param joinPoint * @throws Throwable */ @Before("webLog()") public void doBefore(JoinPoint joinPoint) { try{ // 开始打印请求日志 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); // 打印请求相关参数 log.info("========================================== Start =========================================="); // 打印请求 url log.info("URL : {}", request.getRequestURL().toString()); // 打印 Http method log.info("HTTP Method : {}", request.getMethod()); // 打印调用 controller 的全路径以及执行方法 log.info("Class Method : {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName()); // 打印请求的 IP log.info("IP : {}", request.getRemoteAddr()); // 打印请求入参 String args = new Gson().toJson(joinPoint.getArgs()); log.info("Request Args : {}", args); } catch (Exception e) { log.error("日志打印失败"); } } /** * 在切点之后织入 * @throws Throwable */ @After("webLog()") public void doAfter() throws Throwable { log.info("=========================================== End ==========================================="); // 每个请求之间空一行 log.info(""); } /** * 环绕 * @param proceedingJoinPoint * @return * @throws Throwable */ @Around("webLog()") public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { long startTime = System.currentTimeMillis(); Object result = proceedingJoinPoint.proceed(); // 打印出参 log.info("Response Args : {}", new Gson().toJson(result)); // 执行耗时 log.info("Time-Consuming : {} ms", System.currentTimeMillis() - startTime); return result; } }

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

通过AOP 实现打印全局日志

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

controller

package com.example.springbootorderrabbitmqproducer.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author 康世行 * @Title: * @Package com.example.springbootorderrabbitmqproducer.controller * @Description: 测试日志controller * @date 2021-12-09 8:27 */ @RestController @RequestMapping("/test") public class testController { @GetMapping("/info/{mesg}") public String testLoginfo(@PathVariable("mesg") String mesg){ return mesg; } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

实现效果

方法执行之前

: ========================================== Start ========================================== 2021-12-09 08:39:34.556 INFO 32148 --- [nio-8080-exec-1] c.e.s.AOP.Logs : URL : http://localhost:8080/test/info/ksh 2021-12-09 08:39:34.556 INFO 32148 --- [nio-8080-exec-1] c.e.s.AOP.Logs : HTTP Method : GET 2021-12-09 08:39:34.557 INFO 32148 --- [nio-8080-exec-1] c.e.s.AOP.Logs : Class Method : com.example.springbootorderrabbitmqproducer.controller.testController.testLoginfo 2021-12-09 08:39:34.558 INFO 32148 --- [nio-8080-exec-1] c.e.s.AOP.Logs : IP : 0:0:0:0:0:0:0:1 2021-12-09 08:39:34.560 INFO 32148 --- [nio-8080-exec-1] c.e.s.AOP.Logs : Request Args : ["ksh"]

1

2

3

4

5

6

7

方法执行之后

2021-12-09 08:39:34.564 INFO 32148 --- [nio-8080-exec-1] c.e.s.AOP.Logs : =========================================== End =========================================== 2021-12-09 08:39:34.564 INFO 32148 --- [nio-8080-exec-1] c.e.s.AOP.Logs :

1

2

3

方法执行前后

2021-12-09 08:39:34.564 INFO 32148 --- [nio-8080-exec-1] c.e.s.AOP.Logs : Response Args : "ksh" 2021-12-09 08:39:34.564 INFO 32148 --- [nio-8080-exec-1] c.e.s.AOP.Logs : Time-Consuming : 9 ms

1

2

3

思考

如下代码

思考一,

Object result = proceedingJoinPoint.proceed();方法出现的位置会影响Around的执行吗?

思考二,

Object result = proceedingJoinPoint.proceed(); 方法出现在

long startTime = System.currentTimeMillis();

log.info(“进入指定路径”+proceedingJoinPoint.getTarget()+“controller”);这两行代码下面,方法执行前会输出什么? 先执行Befour还是先执行Around?

/** * 环绕 * @param proceedingJoinPoint * @return * @throws Throwable */ @Around("webLog()") public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { long startTime = System.currentTimeMillis(); log.info("进入指定路径"+proceedingJoinPoint.getTarget()+"controller"); Object result = proceedingJoinPoint.proceed(); // 打印出参 log.info("Response Args : {}", new Gson().toJson(result)); // 执行耗时 log.info("Time-Consuming : {} ms", System.currentTimeMillis() - startTime); log.info("方法执行完毕"); return result; }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

揭晓答案

思考一,

会影响Around的方法执行,Object result = proceedingJoinPoint.proceed() 如果出现在第一行,那么日志就不会打印进入方法之前的日志。(指的是Around方法不会打印进入方法之前的日志,并不会影响Before方法)

原因:

proceedingJoinPoint.proceed() ,proceed() 这个方法是继续执行目标方法,就是被切点切入的方法。可以使用这个方法控制被切方法的运行时机。

思考二,

这个和上面的around代码相反,会在进入方法之前打印 进入了那个controller的全路径,从结果上看around的优先级比Before的优先级高(具体的待研究)

输出结果:

欢迎一键三连,支持下小编!!!

AOP 面向对象编程

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:【Kotlin 初学者】扩展-享受编程
下一篇:Linux系统中网络管理命令和查看网络配置的命令
相关文章