0. 寫在最前面 之前實習天天在寫業務,其中有一個業務是非常的複雜,涉及到了特別多的表。最後測下來,一個介面的時間,竟然要5s多。 當時想寫一個AOP,來計算處理介面花費多長時間,也就是在業務邏輯的前面計算開始的時間,業務邏輯後面計算結束的時間,一相減即可。 但我發覺我竟然忘記怎麼寫了,哎,沒辦法, ...
0. 寫在最前面
之前實習天天在寫業務,其中有一個業務是非常的複雜,涉及到了特別多的表。最後測下來,一個介面的時間,竟然要5s多。
當時想寫一個AOP,來計算處理介面花費多長時間,也就是在業務邏輯的前面計算開始的時間,業務邏輯後面計算結束的時間,一相減即可。
但我發覺我竟然忘記怎麼寫了,哎,沒辦法,在此紀錄,重新撿回吧。
1. 什麼AOP
AOP(Aspect-Oriented Programming, 面向切麵編程): 是一種新的方法論, 是對傳統 OOP(Object-Oriented Programming, 面向對象編程) 的補充.
AOP 的主要編程對象是切麵(aspect), 而切麵模塊化橫切關註點.
在應用 AOP 編程時, 仍然需要定義公共功能, 但可以明確的定義這個功能在哪裡, 以什麼方式應用, 並且不必修改受影響的類. 這樣一來橫切關註點就被模塊化到特殊的對象(切麵)里.
AOP 的好處: 每個事物邏輯位於一個位置, 代碼不分散, 便於維護和升級 業務模塊更簡潔, 只包含核心業務代碼.
簡單來說,AOP就是在不影響現有的業務邏輯下麵,在業務前面後面織入一些其他邏輯。比如,日誌,驗證等等
2. SpringAOP有哪幾種
-
-
@After: 後置通知, 在方法執行之後執行
-
@AfterRunning: 返回通知, 在方法返回結果之後執行
-
@AfterThrowing: 異常通知, 在方法拋出異常之後
-
@Around: 環繞通知, 圍繞著方法執行
假設我們現在有一個介面如下:
1 @RequestMapping("/aa") 2 public String a() { 3 Person person = new Person("zhangsan", 18); 4 redisService.put("person", person); 5 Person ps = redisService.get("person"); 6 return ps.toString(); 7 }
現在產品提了一個需求,我想在這段業務執行前,列印一句話,結束後在列印一句話。
很簡單,我們直接改業務代碼就行了,一個可以兩個可以,但是多了呢,很嚴重影響業務邏輯,此時就可以用到了前置通知和後置通知。
1 @Pointcut(value = "execution (public * com.test.mybatis.demo.web.DepartmentController.*(..))") 2 public void pointCut() { } 3 4 @Before(value = "pointCut()") 5 public void beforeMethod(JoinPoint joinPoint) { 6 String methodName = joinPoint.getSignature().getName(); 7 List<Object> args = Lists.newArrayList(joinPoint.getArgs()); 8 log.info("The method [" + methodName + "] begins with " + args); 9 } 10 11 @After(value = "pointCut()") 12 public void afterMethod(JoinPoint joinPoint) { 13 String methodName = joinPoint.getSignature().getName(); 14 log.info("The method [" + methodName + "] ends"); 15 }
解釋下這段代碼。
@Before,@After 一個是前置通知,一個是後置通知
此時我再訪問這個介面的時候,他的執行流程如下:
beforeMethod ==> 業務邏輯 ==> afterMethod
是不是非常的神奇,AOP的實現方式可以看我這邊博文:動態代理兩種實現方式
3. 回到我們的最初
如何計算一個介面耗時多少呢?
起初我想到可以用前置 + 後置 來做,但最後相減,變數怎麼傳遞不好搞。想過用全局變數,但併發會出現問題。
此時我想到了很少用的環繞通知,最後代碼如下:
1 @Around("pointCut()") 2 public Object aroundMethod(ProceedingJoinPoint joinPoint) { 3 Object res = null; 4 String methodName = joinPoint.getSignature().getName(); 5 try { 6 // 前置通知 7 Stopwatch stopwatch = Stopwatch.createStarted(); 8 // 執行目標方法 9 res = joinPoint.proceed(); 10 // 後置通知 11 long duration = stopwatch.elapsed(TimeUnit.MILLISECONDS); 12 log.info("{} 執行時長: {}", methodName, duration); 13 } catch (Throwable throwable) { 14 throwable.printStackTrace(); 15 } 16 return res; 17 }
每次執行一個方法,他都會將你的執行時間列印出來。結果如下:
1 // 前置通知 2 2019-01-24 21:17:52.802 INFO 224 --- [nio-8080-exec-5] com.test.mybatis.demo.AOP.TestAOP : The method [testAOP] begins 3 // 業務邏輯 4 這裡是業務邏輯 5 // 環繞通知 6 2019-01-24 21:17:53.303 INFO 224 --- [nio-8080-exec-5] com.test.mybatis.demo.AOP.TestAOP : testAOP 執行時長: 500 7 // 後置通知 8 2019-01-24 21:17:53.303 INFO 224 --- [nio-8080-exec-5] com.test.mybatis.demo.AOP.TestAOP : The method [testAOP] ends
4. 寫在最後
最後我想將這個計算介面時間的AOP定義成一個註解,只要我在介面上加這個註解,他就會列印時間,否則就不列印。
這是後期的一個目標。共勉