今天是劉小愛自學JAVA的第123天。
感謝你的觀看,謝謝你。
學過很多面向XX編程,比如:
面向過程編程,面向?qū)ο缶幊蹋嫦蚪涌诰幊蹋F(xiàn)在又是面向切面編程。
但是不管如何,說來說去最終都是面向搜索引擎編程:面向百度編程,面向谷歌編程。
今日學習內(nèi)容安排:
- AOP的引入,它到底是干嘛的?
- AOP面向切面編程的思想概述,以及其常見術(shù)語的解釋說明。
- 兩種AOP底層實現(xiàn)機制,同時也是對動態(tài)代理的再一次回顧學習。
本來是打算將AOP知識點糅合到一篇文章中說明的,但是內(nèi)容實在是太多了,寫了近三千字一半都還沒有學到,看來還是得慢慢來了。
一、AOP的引入
在學它之前,我們先要搞清楚它是干嘛的?
dao層的方法基本都是增刪改查,現(xiàn)在需要將所有方法都增加打印日志的功能,怎么辦?
如果我們每個方法里面都實現(xiàn)打印日志的功能,那也太復雜了,所以選擇封裝:
①方法的封裝
我們將打印日志的功能封裝到一個特有方法中,只需要在其它方法中調(diào)用該方法即可。
但是這樣就有一個很大的問題:
dao層不只有userDao這個類,還有其它的類,也需要打印日志的功能,那怎么辦?
②繼承
我們將打印日志的功能封裝到一個類中,哪個類需要該方法就繼承它即可,根據(jù)繼承的原則:子類可以直接使用父類的方法。
但是代碼還是有問題,會出現(xiàn)代碼的侵入。
有沒有方法可以不用修改類中方法的任何內(nèi)容,就能實現(xiàn)方法的拓展?
有,就是代理類的使用。
注意:我舉的這些例子都是偽代碼,并不代表本身的業(yè)務(wù)邏輯,只是為了引出AOP的概念。
③代碼的侵入
我們想給方法增加功能,使用繼承的話都需要在對應方法中調(diào)用一個打印日志的方法。
對方法本身修改了,有代碼侵入,這是不符合OCP原則的,即對擴展開放,對修改關(guān)閉:你增強我的功能可以,但你不可以修改我。
④使用代理
在被代理類方法的基礎(chǔ)上,拓展了一個打印日志的方法,本身的方法并沒有發(fā)生任何變化。
當然這里也是偽代碼,并沒有使用到動態(tài)代理,文章后面有更詳細的一步步說明。
我們以繼承->代理的這種代碼變化過程,引出AOP面向切面編程的概念。
二、AOP概述及相關(guān)術(shù)語
AOP全稱Aspect Oriented Programing,翻譯為面向切面編程,它是一種編程思想。
我們都知道Java是一門面向?qū)ο缶幊痰模碠OP全稱Object Oriented Programming。
AOP是OOP思想上的延續(xù),采取橫向抽取機制,取代了傳統(tǒng)縱向繼承體系重復性代碼的編寫。
簡單的理解就是,它的作用和繼承很像,但是它比繼承要更強,用一句來說明AOP就是:
基于原有目標對象,創(chuàng)建代理對象,在不修改原對象代碼情況下,通過代理對象調(diào)用增強功能的代碼,從而對原有方法進行增強 。
關(guān)于AOP編程相關(guān)術(shù)語
這些術(shù)語太生澀難懂了,每一個概念涉及到的知識面還很廣,想要完全弄懂太難了。
這里用一個例子來做說明,當然說明并不是很準確,但是對于新手來說方便理解記憶。
①目標對象Target
也就是需要被增強的對象。
②織入Weaving
根據(jù)目標對象來創(chuàng)建代理對象的整個過程。
③代理對象Proxy
即根據(jù)目標對象生成的代理對象。
④連接點JoinPoint
所謂連接點是指那些被攔截到的點。
就可以理解成對象中的方法,因為在Spring中,只支持方法類型的連接點。
⑤切入點PointCut
所謂切入點就是連接點的一部分,即需要被攔截的連接點就是切入點。
就可以理解成對象中需要增強的方法。
⑥通知Advice
也就是增強的方法,例子中就是記錄日志。
通知分為前置通知、后置通知、異常通知、最終通知、環(huán)繞通知,這些后續(xù)會講述。
⑦切面Aspect
是通知和切入點的結(jié)合,通知和切入點共同定義了關(guān)于切面的全部內(nèi)容。它的功能、在何時和何地完成其功能?說白了也就是:
如何將增強方法添加到對應的方法中?
此外還有一個術(shù)語叫:引介Introduction
在不修改類代碼的前提下, Introduction可以在運行期為類動態(tài)地添加一些方法或?qū)傩裕@個實際開發(fā)中基本涉及不到。
AOP是基于動態(tài)代理的,基于兩種動態(tài)代理機制:JDK動態(tài)代理和CGLIB動態(tài)代理。
三、JDK動態(tài)代理實現(xiàn)AOP
當然JDK動態(tài)代理很少使用,但是還是都寫下,就當是對動態(tài)代理知識點的一個回顧。
創(chuàng)建工廠類,該類可以獲取代理類對象:
①獲取代理對象方法
通過代理工廠的該方法就可以獲取一個代理對象,為了通用性將返回值設(shè)定為Object。
②實例化代理類對象
Proxy類的靜態(tài)方法newProxyInstance(),根據(jù)方法名也能知道它是干嘛的,基本上動態(tài)代理的核心就是這個方法,參數(shù)有三個:
- 目標對象的類加載器。
- 目標對象實現(xiàn)的接口有哪些。
- 調(diào)用處理器。
當然,其代碼編寫有更優(yōu)的方式,在Cglib動態(tài)代理中會說明,此處就使用最原始的方式。
③調(diào)用處理器
InvocationHandler是一個接口,使用匿名內(nèi)部類的方式獲取其對象,其有一個方法叫invoke,該方法也有三個參數(shù)。
如果方法名是我們需要增強的方法,那么我們給它增加一個功能,也就是④。
如果不是,那么調(diào)用自己就好了,也就是method.invoke(target,args)。
代碼寫完,做個測試
⑤功能測試
因為在動態(tài)代理中我們只選擇對queryAll方法增強,所以用代理對象調(diào)用queryAll方法時會額外輸出“記錄日志”。
而update方法不增強,就只會執(zhí)行本身的功能,也就是“更新數(shù)據(jù)”。
當然Jdk動態(tài)代理有一個局限,就是必須要有接口才行,所以就引出了CGLIB的使用。
四、CGLIB動態(tài)代理
CGLIB(Code Generation Library)是一個強大的,高性能的開源項目。
其作用最直接的解釋就是:不需要接口也可以實現(xiàn)動態(tài)代理。
①獲取代理對象生成器
Enhancer,增強器的意思,也就是通過它來實現(xiàn)方法的增強。
②設(shè)置目標對象的Class對象
該參數(shù)是目標對象的Class對象,不是類加載器,和Jdk動態(tài)代理有一定的區(qū)別。
③設(shè)置回調(diào)函數(shù)
Jdk動態(tài)代理中的三個參數(shù):類加載器、接口以及調(diào)用處理器,Cglib中不需要接口,該參數(shù)就相當于jdk動態(tài)代理中的調(diào)用處理器。
setCallback方法的參數(shù)需要該接口的實現(xiàn)類對象,我們可以直接使用匿名內(nèi)部類的方式作為參數(shù),就和調(diào)用處理器一樣。
但是在本類中實現(xiàn)這個接口,不就有了一個現(xiàn)成的實現(xiàn)類么?而this表示誰調(diào)用我就是誰,本類或者本類的子類都行。
這里進一步優(yōu)化代碼的編寫,上述Jdk動態(tài)代理中也可以這樣優(yōu)化。
④intercept方法
這是MethodInterceptor接口中的一個方法,intercept,翻譯就是攔截的意思。
其參數(shù)和Jdk中的調(diào)用處理器基本一樣。
⑤生成代理對象
enhancer調(diào)用create()生成代理對象。
代碼寫完,做個測試
⑥方法測試
通過運行結(jié)果我們可以發(fā)現(xiàn):和Jdk動態(tài)代理能達到一樣增強選定方法的效果。
注意:目標對象CustomerServicePlus并沒有實現(xiàn)接口,如果使用Jdk動態(tài)代理是不行的,得使用Cglib動態(tài)代理才可以。
最后
謝謝你的觀看。
如果可以的話,麻煩幫忙點個贊,謝謝你。