今天是劉小愛自學JAVA的第123天。
感謝你的觀看,謝謝你。

學過很多面向XX編程,比如:
面向過程編程,面向對象編程,面向接口編程,現在又是面向切面編程。
但是不管如何,說來說去最終都是面向搜索引擎編程:面向百度編程,面向谷歌編程。
今日學習內容安排:
- AOP的引入,它到底是干嘛的?
- AOP面向切面編程的思想概述,以及其常見術語的解釋說明。
- 兩種AOP底層實現機制,同時也是對動態代理的再一次回顧學習。
本來是打算將AOP知識點糅合到一篇文章中說明的,但是內容實在是太多了,寫了近三千字一半都還沒有學到,看來還是得慢慢來了。
一、AOP的引入
在學它之前,我們先要搞清楚它是干嘛的?
dao層的方法基本都是增刪改查,現在需要將所有方法都增加打印日志的功能,怎么辦?
如果我們每個方法里面都實現打印日志的功能,那也太復雜了,所以選擇封裝:

①方法的封裝
我們將打印日志的功能封裝到一個特有方法中,只需要在其它方法中調用該方法即可。
但是這樣就有一個很大的問題:
dao層不只有userDao這個類,還有其它的類,也需要打印日志的功能,那怎么辦?
②繼承
我們將打印日志的功能封裝到一個類中,哪個類需要該方法就繼承它即可,根據繼承的原則:子類可以直接使用父類的方法。
但是代碼還是有問題,會出現代碼的侵入。
有沒有方法可以不用修改類中方法的任何內容,就能實現方法的拓展?
有,就是代理類的使用。
注意:我舉的這些例子都是偽代碼,并不代表本身的業務邏輯,只是為了引出AOP的概念。

③代碼的侵入
我們想給方法增加功能,使用繼承的話都需要在對應方法中調用一個打印日志的方法。
對方法本身修改了,有代碼侵入,這是不符合OCP原則的,即對擴展開放,對修改關閉:你增強我的功能可以,但你不可以修改我。
④使用代理
在被代理類方法的基礎上,拓展了一個打印日志的方法,本身的方法并沒有發生任何變化。
當然這里也是偽代碼,并沒有使用到動態代理,文章后面有更詳細的一步步說明。
我們以繼承->代理的這種代碼變化過程,引出AOP面向切面編程的概念。
二、AOP概述及相關術語
AOP全稱Aspect Oriented Programing,翻譯為面向切面編程,它是一種編程思想。
我們都知道Java是一門面向對象編程的,即OOP全稱Object Oriented Programming。
AOP是OOP思想上的延續,采取橫向抽取機制,取代了傳統縱向繼承體系重復性代碼的編寫。
簡單的理解就是,它的作用和繼承很像,但是它比繼承要更強,用一句來說明AOP就是:
基于原有目標對象,創建代理對象,在不修改原對象代碼情況下,通過代理對象調用增強功能的代碼,從而對原有方法進行增強 。
關于AOP編程相關術語
這些術語太生澀難懂了,每一個概念涉及到的知識面還很廣,想要完全弄懂太難了。
這里用一個例子來做說明,當然說明并不是很準確,但是對于新手來說方便理解記憶。

①目標對象Target
也就是需要被增強的對象。
②織入Weaving
根據目標對象來創建代理對象的整個過程。
③代理對象Proxy
即根據目標對象生成的代理對象。
④連接點JoinPoint
所謂連接點是指那些被攔截到的點。
就可以理解成對象中的方法,因為在Spring中,只支持方法類型的連接點。
⑤切入點PointCut
所謂切入點就是連接點的一部分,即需要被攔截的連接點就是切入點。
就可以理解成對象中需要增強的方法。
⑥通知Advice
也就是增強的方法,例子中就是記錄日志。
通知分為前置通知、后置通知、異常通知、最終通知、環繞通知,這些后續會講述。
⑦切面Aspect
是通知和切入點的結合,通知和切入點共同定義了關于切面的全部內容。它的功能、在何時和何地完成其功能?說白了也就是:
如何將增強方法添加到對應的方法中?
此外還有一個術語叫:引介Introduction
在不修改類代碼的前提下, Introduction可以在運行期為類動態地添加一些方法或屬性,這個實際開發中基本涉及不到。
AOP是基于動態代理的,基于兩種動態代理機制:JDK動態代理和CGLIB動態代理。
三、JDK動態代理實現AOP
當然JDK動態代理很少使用,但是還是都寫下,就當是對動態代理知識點的一個回顧。
創建工廠類,該類可以獲取代理類對象:

①獲取代理對象方法
通過代理工廠的該方法就可以獲取一個代理對象,為了通用性將返回值設定為Object。
②實例化代理類對象
Proxy類的靜態方法newProxyInstance(),根據方法名也能知道它是干嘛的,基本上動態代理的核心就是這個方法,參數有三個:
- 目標對象的類加載器。
- 目標對象實現的接口有哪些。
- 調用處理器。
當然,其代碼編寫有更優的方式,在Cglib動態代理中會說明,此處就使用最原始的方式。
③調用處理器
InvocationHandler是一個接口,使用匿名內部類的方式獲取其對象,其有一個方法叫invoke,該方法也有三個參數。
如果方法名是我們需要增強的方法,那么我們給它增加一個功能,也就是④。
如果不是,那么調用自己就好了,也就是method.invoke(target,args)。
代碼寫完,做個測試

⑤功能測試
因為在動態代理中我們只選擇對queryAll方法增強,所以用代理對象調用queryAll方法時會額外輸出“記錄日志”。
而update方法不增強,就只會執行本身的功能,也就是“更新數據”。
當然Jdk動態代理有一個局限,就是必須要有接口才行,所以就引出了CGLIB的使用。
四、CGLIB動態代理
CGLIB(Code Generation Library)是一個強大的,高性能的開源項目。
其作用最直接的解釋就是:不需要接口也可以實現動態代理。

①獲取代理對象生成器
Enhancer,增強器的意思,也就是通過它來實現方法的增強。
②設置目標對象的Class對象
該參數是目標對象的Class對象,不是類加載器,和Jdk動態代理有一定的區別。
③設置回調函數
Jdk動態代理中的三個參數:類加載器、接口以及調用處理器,Cglib中不需要接口,該參數就相當于jdk動態代理中的調用處理器。
setCallback方法的參數需要該接口的實現類對象,我們可以直接使用匿名內部類的方式作為參數,就和調用處理器一樣。
但是在本類中實現這個接口,不就有了一個現成的實現類么?而this表示誰調用我就是誰,本類或者本類的子類都行。
這里進一步優化代碼的編寫,上述Jdk動態代理中也可以這樣優化。
④intercept方法
這是MethodInterceptor接口中的一個方法,intercept,翻譯就是攔截的意思。
其參數和Jdk中的調用處理器基本一樣。
⑤生成代理對象
enhancer調用create()生成代理對象。
代碼寫完,做個測試

⑥方法測試
通過運行結果我們可以發現:和Jdk動態代理能達到一樣增強選定方法的效果。
注意:目標對象CustomerServicePlus并沒有實現接口,如果使用Jdk動態代理是不行的,得使用Cglib動態代理才可以。
最后
謝謝你的觀看。
如果可以的話,麻煩幫忙點個贊,謝謝你。