javascript
Spring-AOP基础知识
- 導讀
- 概述
- 帶有橫切邏輯的實例
- JDK動態代理
- CGLib動態代理
- 代理知識小結
導讀
Spring-AOP基礎知識
Java-JDK動態代理
Java-CGLib動態代理
概述
Spring AOP 使用動態代理技術在運行期織入增強的代碼.
為了解密SpringAOP底層的工作機制,我們需要先學習下涉及到的JAVA知識。
Spring使用兩種代理機制:
之所以需要兩種代理,是因為JDK本身只能提供接口的代理,而不支持類的代理。
帶有橫切邏輯的實例
代碼已托管到Github—> https://github.com/yangshangwei/SpringMaster
來看個實例,我們以性能監控為例子,在調用每個目標類方法時啟動方法的性能監視,在目標類方法調用完成時記錄方法的花費時間。
我們首先來看下包含性能監視橫切代碼的 ForumService
package com.xgj.aop.base.instance;/*** * * @ClassName: ForumServiceImpl* * @Description: ForumService實現類* * @author: Mr.Yang* * @date: 2017年8月12日 下午4:14:30*/ public class ForumServiceImpl implements ForumService {@Overridepublic void removeTopic(int topicId) {// 1-(1)開始對removeTopic方法的監控PerformanceMonitor.begin("removeTopic");// 模擬業務邏輯System.out.println("模擬刪除Topic");try {Thread.currentThread().sleep((long) (Math.random() * 1000 * 10));} catch (InterruptedException e) {e.printStackTrace();}// 1-(2)結束對removeTopic方法的監控PerformanceMonitor.end("removeTopic");}@Overridepublic void removeForum(int forumId) {// 2-(1)開始對removeForum法的監控PerformanceMonitor.begin("removeForum");// 模擬業務邏輯System.out.println("模擬刪除forum");try {Thread.currentThread().sleep((long) (Math.random() * 1000 * 10));} catch (InterruptedException e) {e.printStackTrace();}// 2-(2)結束對removeForum法的監控PerformanceMonitor.end("removeForum");}}ForumServiceImpl 實現了ForumService接口。
package com.xgj.aop.base.instance;/*** * * @ClassName: ForumService* * @Description: ForumService接口* * @author: Mr.Yang* * @date: 2017年8月12日 下午4:13:31*/ public interface ForumService {/*** * * @Title: removeTopic* * @Description: 根據topicId刪除Topic* * @param topicId* * @return: void*/void removeTopic(int topicId);/*** * * @Title: removeForum* * @Description: 根據forumId刪除Forum* * @param forumId* * @return: void*/void removeForum(int forumId); }其中1-(1) 1-(2) 2-(1)2-(2)標注的是具有橫切邏輯特征的代碼,每個Service類和每個業務方法體的前后都執行相同的代碼邏輯:方法啟動前啟用PerformanceMonitor,方法調用后通知PerformanceMonitor結束性能監視并記錄性能監視結果。
PerformanceMonitor是性能監視的實現類
package com.xgj.aop.base.instance;public class PerformanceMonitor {// 通過一個ThreadLocal保存與調用線程相關的性能監視信息private static ThreadLocal<MethoPerformance> performanceLocal = new ThreadLocal<MethoPerformance>();/*** * * @Title: begin* * @Description: 啟動對某一目標方法的性能監視* * @param method* * @return: void*/public static void begin(String method) {System.out.println("begin to monitor:" + method);MethoPerformance methoPerformance = new MethoPerformance(method);performanceLocal.set(methoPerformance);}/*** * * @Title: end* * @Description: 輸出性能監視結果* * @param method* * @return: void*/public static void end(String method) {System.out.println("finish monitor:" + method);MethoPerformance methoPerformance = performanceLocal.get();// 打印出方法性能監視的結果信息methoPerformance.printPerformance();} }我們通過ThreadLocal將非線程安全類改造為線程安全的類。
PerformanceMonitor類中兩個方法 begin和end ,其中method規定為目標類方法的全限定名。 兩個方法必須配套使用。
用于記錄性能監視信息的MethoPerformance 如下:
package com.xgj.aop.base.instance;public class MethoPerformance {private long beginTime;private long endTime;private String methodName;/*** * * @Title:MethoPerformance* * @Description:構造函數* * @param methodName*/public MethoPerformance(String methodName) {super();this.methodName = methodName;this.beginTime = System.currentTimeMillis();}/*** * * @Title: printPerformance* * @Description: 計算耗時* * * @return: void*/public void printPerformance() {endTime = System.currentTimeMillis();long cost = endTime - beginTime;System.out.println(methodName + " costs " + cost / 1000 + "秒\n");} }測試類:
package com.xgj.aop.base.instance;public class ForumServiceTest {public static void main(String[] args) {ForumService forumService = new ForumServiceImpl();forumService.removeTopic(1);forumService.removeForum(2);}}運行結果:
問題:通過這個實例,我們可以看到當某個方法需要進行性能監視時,必須調整方法代碼,在方法體前后分別添加開啟性能監視和結束性能監視的代碼。 這些非業務邏輯的性能監視代碼破壞了業務實現類中業務邏輯的純粹性。
改進:我們希望通過代理的方式將業務類方法中開啟和結束性能監視的橫切代碼從業務類中完全移除,并通過JDK或者CGLib動態代理技術將橫切代碼動態的織入到目標方法的相應位置。
JDK動態代理
詳情請訪問另一篇博客: Java-JDK動態代理
CGLib動態代理
詳情請訪問另一篇博客: Java-CGLib動態代理
代理知識小結
Spring AOP的底層就是通過使用JDK或者CGLib動態代理技術為目標Bean織入橫切邏輯的。
總結一下動態創建代理對象:
雖然通過PerformanceHandle或者CglibProxy實現了性能監視橫切邏輯的動態織入,但是這種實現方式有3個明顯需要改進的地方
目標類的所有方法都添加了性能監視橫切邏輯,而有的時候這并不是我們所期望的,我們可能只希望對業務類中的某些特定方法添加橫切邏輯
通過硬編碼的方式指定了織入橫切邏輯的織入點,即在目標類業務方法的開始和結束前織入代碼
手工編寫代理實例的創建過程,在為不同類創建代理時,需要分別編寫相應的創建代碼,無法做到通用。
以上3個問題在AOP中占有重要的地位,因為SpringAOP的主要工作就是圍繞以上3點展開的: Spring AOP 通過Pointcut(切點)指定在哪些類的哪些方法上織入橫切邏輯,通過Advice(增強)描述橫切邏輯和方法的具體織入點(方法前、方法后、方法的兩端等)。
此外,Spring通過Advisor(切面)將Point和Advice組裝起來,有了Advisor信息,SPring就可以利用JDK或者CGLib動態代理結束采用統一的方式為目標Bean創建織入切面的代理對象了。
對應singleton的代理對象或者具有實例池的代理,因無需頻繁創建對象,比較適合采用CGLib動態代理技術,反之則比較適合采用JDK動態代理技術
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的Spring-AOP基础知识的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring-AOP概述
- 下一篇: Spring-AOP 通过配置文件实现