圖文詳解 Spring AOP,看完必懂!
往期熱門文章:
3、@Bean與@Component 同時作用同一個類,會怎么樣?
4、閑魚面試:Thread.sleep(0) 到底有什么用?
來源:blog.csdn.net/duxd185120/article/details/109210224
學習一個模塊的設計主要是看接口設計,通過接口設計我們就能夠從整體知道模塊怎么實現(xiàn)的,具體實現(xiàn)就是組裝這些接口來進行實現(xiàn)的,知道了模塊接口設計,實現(xiàn)也就變得很簡單了。
本文主要從aop背景出發(fā)點,來自己去想需要哪些接口,就能夠描述一個模塊的功能設計規(guī)則。
AOP產生背景
使用面向對象編程 ( OOP )有一些弊端,當需要為多個不具有繼承關系的對象引人同一個公共行為時,例如日志、安全檢測等,我們只有在每個對象里引用公共行為,這樣程序中就產生了大量的重復代碼,程序就不便于維護了。
所以就有了一個對面向對象編程的補充,即面向方面編程 ( AOP ), AOP 所關注的方向是橫向的,區(qū)別于 OOP 的縱向。
什么是AOP
什么是面向方面編程,3個過程:
找到橫切點:首要目標確定在程序的哪個位置進行橫切邏輯 橫切邏輯(業(yè)務代碼):橫切邏輯代碼,這個就是橫切業(yè)務代碼,與aop無關 織入:將橫切邏輯織入到橫切點
開發(fā)者主要關心的是橫切邏輯的編寫,只需要很少的代碼編寫確定橫切點有哪些,而不需要去為每個橫切點添加橫切邏輯,不然就是面向對象編程了。
既然是橫向的編程,那么在我們的程序中,哪些可以作為橫線切入點呢?
看下示例代碼:
public?class?Test?{
????public?static?void?main(String[]?args)?{
????????//@1
???????B?b?=?new?B();
???????//@2
???????b.method();
???????//@3
????????B.say();
????}
????static?class?B?{
????????//字段
????????//@4
????????private?String?name;
????????//構造方法
????????public?B()?{
????????????//@1.1
????????????}
????????//對象方法
????????public?void?method(){
????????????//@2.2
????????}
????????//靜態(tài)方法
????????static?void?say(){
????????????//@3.3
????????}
????}
}
所以我們可以將橫切點主要分為兩大類:字段、方法。方法又分為很多種,
橫切點有很多地方,從代碼上看得見的,有如下幾個地方:
使用構造函數(shù)創(chuàng)建對象 構造函數(shù)執(zhí)行 對象方法調用 對象方法執(zhí)行 靜態(tài)方法調用 靜態(tài)方法執(zhí)行 反射讀寫對象字段
目標1:找到橫切點
那么怎么去定義一個橫切點呢?怎么用一個接口來描述一個橫切點呢?
在Java中,一切皆對象,在Java中一個類有2方面內容:字段、方法(構造函數(shù)、對象方法、靜態(tài)方法),java中使用AccessibleObject來抽象公共行為。方法:就是一段可以執(zhí)行的程序,一段代碼。
所以在橫切點接口中,首先一個功能就是返回給用戶當前橫切點,有兩種情況:
如果橫切點作用于對象(對象字段、對象方法、構造函數(shù)),則不僅需要返回AccessibleObject,還需要返回當前對象,因為調用通過反射調用對象方法需要傳入當前對象。 如果橫切點作用于類,則僅返回AccessibleObject即可。
另一個接口功能就是要不要考慮在橫切點來控制多個橫切邏輯的調用。這個可以有框架支持,也可以由橫切點控制。這對應的就是責任鏈模式的API設計。比如tomcat中的Filter鏈式調用就是以集合形式調用;netty中的Handler組織就是以鏈表形式。如果是以集合形式調用,則在橫切點接口需要定義一個方法來鏈式調用。(aop聯(lián)盟的JoinPoint采用是集合形式調用)
那么AOP聯(lián)盟使用JointPoint接口來定義橫切點。
public?interface?Joinpoint?{
?Object?proceed()?throws?Throwable;
?Object?getThis();
?AccessibleObject?getStaticPart();
}
Object proceed() throws Throwable: 鏈式調用橫切點
Object getThis(); 返回連接點當前對象。如果當前連接點是靜態(tài),比如靜態(tài)方法,則該方法返回null,因為反射不需要對象,而且靜態(tài)方法是通過類調用的,壓根就沒有對象,所以返回null。spring aop不支持靜態(tài)方法的攔截,所以在spring中這里返回的就是目標對象(被代理對象)
AccessibleObject getStaticPart(); 返回連接點靜態(tài)部分,對于連接點是方法,返回的就是Method對象。
現(xiàn)在對連接點的設計比較清晰了,然后就是對連接點的擴展了,比如可執(zhí)行程序(構造方法、Method)的子接口,字段的子接口(aop聯(lián)盟沒有定義,只有方法級別的)。
AOP聯(lián)盟對連接點接口的設計:

比如在MethodInvocation,就是返回Method。
目標2:橫切邏輯(增強)抽象定義
增強的抽象,其實就需要連接點信息,畢竟增強是要投入到一個地方的,所以需要連接點信息。
在aop聯(lián)盟的接口定義:
Advice作為一個tag標識,在aop聯(lián)盟中使用攔截器來作為增強的命名,這里完全可以去掉Interceptor,而直接定義一個MethodAdvice。之所以定義為Interceptor,是因為攔截器命名更符合編程命名規(guī)范,讓人從命名就知道接口功能。
在MethodInterceptor,傳入連接點信息(因為是方法攔截,所以這里是方法級別的連接點接口定義)
Object?invoke(MethodInvocation?invocation)?throws?Throwable;
目標3:織入
首先就是怎么織入。織入由兩種方案。
靜態(tài)織入:采用自定義類加載器機制。自定義類加載器根據(jù)織入規(guī)則在加載class文件期間對class文件動手織入橫切邏輯,然后將改動后的class文件交給JVM運行。 動態(tài)織入:由多種選擇,動態(tài)代理(JDK Proxy)、動態(tài)字節(jié)碼生成技術(cglib)
spring采用動態(tài)織入。動態(tài)織入就是生成代理對象,代理對象中維護了當前連接點所有攔截器,然后調用目標方法時被代理類攔截,在代理類中作aop功能。
來一個完整的流程圖:
Spring AOP的實現(xiàn)基于AOP聯(lián)盟接口標準設計實現(xiàn)的,全局看下aopalliance有哪些接口以及接口的API設計,我們上面已經分析完了。
AOP聯(lián)盟的接口很少:
最近熱文閱讀:
1、面試官:內存耗盡后Redis會發(fā)生什么 ? 2、監(jiān)控告警滿飛天,運維在家睡到自然醒... 3、@Bean與@Component 同時作用同一個類,會怎么樣? 4、閑魚面試:Thread.sleep(0) 到底有什么用? 5、身為程序員碰到最奇葩的需求是怎樣的? 6、因為BitMap,白白搭進去8臺服務器... 7、面試官:private修飾的方法可以通過反射訪問,那么private的意義是什么? 8、如何選擇合適的分布式ID生成方案 9、面試題:為什么數(shù)據(jù)庫連接池不采用 IO 多路復用? 10、SpringBoot 配置文件敏感信息如何加密? 關注公眾號,你想要的Java都在這里
