每天學習一點點 編程PDF電子書免費下載: http://www.shitanlife.com/code
什么是AOP
1
AOP(Aspect Oriented Programming),即面向切面編程,其是OOP(Object Oriented Programming,面向對象編程)的補充和完善。在面向對象編程的世界中,我們很容易理解OOP的思想,簡單來說,OOP引入封裝、繼承、多態等概念來建立一種對象層次結構,這種層次結構是縱向的。雖然OOP允許開發者定義縱向的關系,但並不適合定義橫向的關系,例如日志功能。日志代碼往往橫向地散布在所有對象層次中,而與它對應的對象的核心功能關系不大,對於其他類型的代碼,如安全性檢查、異常處理、事務處理等也都是如此,這種散布在各處的重復的代碼被稱為橫切邏輯,在OOP設計中,它導致了大量代碼的重復,不利於各個功能模塊的重用。
AOP技術則恰恰相反,它利用一種稱為"橫切"的技術,剖解開封裝的對象內部,並將那些影響了多個類的公共行為封裝到一個可重用模塊之中,並將其命名為"Aspect",即切面。所謂"切面",簡單說就是將那些被多個業務模塊所共同調用的邏輯封裝起來,以達到減少重復代碼,降低模塊之間的耦合度,並提高系統的可維護性的目的。
使用"橫切"技術,AOP把軟件系統分為兩個部分:核心關注點和橫切關注點。業務處理的主要流程是核心關注點,與業務邏輯關系不大的部分是橫切關注點。橫切關注點的一個特點是,他們經常發生在核心關注點的多處,而各處基本相似,比如權限認證、日志、事務管理等。AOP的作用在於分離系統中的各種關注點,將核心關注點和橫切關注點分離開來。
AOP示例2
前面說了這么多,接下來我們就用Spring AOP來實現簡單的日志記錄功能吧。假如我們已經有了一個功能完善的用戶登陸接口,現在我們需要在用戶調用登陸接口的前后記錄下用戶的登錄行為日志。要實現該功能,最簡單的方法就是在原有的登錄邏輯里加入日志記錄代碼,但是這樣一來勢必需要對原有的登錄邏輯進行修改,容易引入新的bug,因此我們決定使用AOP來實現日志記錄的功能。在本實例中,我們搭建一個Spring Boot工程,並引入Spring AOP依賴,pom文件依賴關系如下:
接着我們實現最原始的登錄邏輯:
登錄邏輯十分簡單,首先判斷用戶是否為合法用戶,如果是則可以正常登錄,如果不是則禁止登錄。
由於我們要在登錄邏輯前后加入日志功能,所以我們需要編寫一個環繞通知:
可以看到,我們的環繞增強針對login方法進行橫切邏輯的織入,在調用目標對象的前后,分別對用戶登錄日志進行記錄。
接下來寫一個測試類看一下效果:
結果輸出如下:
可以看到,我們通過AOP很方便地實現了日志記錄功能。
AOP失效的情形3
接下來假如我們又有了一個新需求,就是要對不合法用戶做些特殊的處理,比如說統計下不合法用戶調用登陸接口的次數。由於直接修改原有的登錄邏輯有很多弊端,所以我們還是選擇通過AOP來實現該功能。這可以通過編寫一個返回增強來實現:
我們對isLegal方法進行增強,先拿到isLegal方法的返回值,再根據該返回值決定是否需要累加登錄次數。
接下來我們還是用上一節的測試類來測試一下,我們直接看結果:
這個時候詭異的事情發生了。明明user_1為非法用戶,但是為何沒有對其登錄次數進行累加呢?AOP為何會失效呢?下文將為你解開謎團。
AOP為何失效
4
之所以會出現上述AOP失效的現象,歸根到底是由於AOP的實現機制導致的。Spring AOP采用代理的方式實現AOP,我們編寫的橫切邏輯被添加到動態生成的代理對象中,只要我們調用的是代理對象,則可以保證調用的是被增強的代理方法。而在代理對象中,不管你的橫切邏輯是怎樣的,也不管你增加了多少層的橫切邏輯,有一點可以確定的是,你終歸會調用目標對象的同一方法來調用原始的業務邏輯。
如果目標對象中的原始方法依賴於其他對象,那么Spring會注入所依賴對象的代理對象,從而保證依賴的對象的橫切邏輯能夠被正常織入。而一旦目標對象調用的是自身的其他方法時,問題就來了,這種情況下,目標對象調用的並不是代理對象的方法,故被調用的方法無法織入橫切邏輯。
如上圖所示,method1和method2方法是同個類中的方法,當外部通過代理對象調用method1時,最終會調用目標對象的method1方法,而在目標對象的method1方法中調用method2方法時,最終調用的是目標對象的method2方法,而不是代理對象的method2方法,故而針對method2的AOP增強失效了。
如何避免AOP失效5
要解決上述Spring AOP失效的問題,有兩個方法,一個是將isLegal方法跟login方法寫在不同的類里,這樣一來,當login方法調用isLegal方法時,Spring會注入相應的代理對象,從而可以調用到isLegal方法的代理邏輯。另一個方法是在調用isLegal方法時先獲取當前上下文的代理對象,再通過該代理對象調用被增強了的isLegal方法,這樣一來也能解決AOP失效的問題。實際上Spring AOP為我們提供了獲取當前上下文代理對象的方法,使用起來非常方便,首先需要在AOP配置里暴露代理對象,在Spring Boot中可以通過注解@EnableAspectJAutoProxy(exposeProxy = true)進行配置:
然后修改login方法,通過AopContext獲取當前上下文代理對象,再通過該代理對象調用isLegal方法:
最后我們運行測試類看下效果:
可以看到,現在已經可以實現對非法用戶的登錄次數進行累加了,這樣就解決了上述AOP失效的問題。
每天學習一點點 編程PDF電子書免費下載: http://www.shitanlife.com/code