通过AOP 实现打印全局日志

网友投稿 579 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

通过AOP 实现打印全局日志

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

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系统中网络管理命令和查看网络配置的命令
相关文章