通過AOP 實現打印全局日志
常用注解
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
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
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的優先級高(具體的待研究)
輸出結果:
歡迎一鍵三連,支持下小編?。。?/p>
AOP 面向對象編程
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。