您好,登錄后才能下訂單哦!
這篇文章主要介紹“Spring AOP底層原理及代理模式實例分析”,在日常操作中,相信很多人在Spring AOP底層原理及代理模式實例分析問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Spring AOP底層原理及代理模式實例分析”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
AOP 就是面向切面編程,是 OOP(面向?qū)ο缶幊?的延續(xù)。
利用 AOP 可以對業(yè)務(wù)邏輯的各個部分進行隔離,從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低,提高程序可用性,同時也提高了開發(fā)效率。
通俗一點說,不用修改原代碼,可以給原代碼增加新的功能。
AOP 底層原理是使用動態(tài)代理。
那代理是什么?有動態(tài)代理,那是不是還有靜態(tài)代理?
就是為一個目標對象提供一個代理對象,并由代理對象控制對目標對象的引用。使用代理對象,是為了在不修改目標對象的基礎(chǔ)上,增強目標對象的業(yè)務(wù)邏輯。
比如目標對象 A,代理對象是 B。
那么現(xiàn)在 B 對 A 進行引用,可以實現(xiàn) A 有的功能。
另外,B 還可以在自身進行一些新功能,最終不需要修改目標對象 A 。
而代理分為靜態(tài)代理和動態(tài)代理,區(qū)別是:
靜態(tài)代理有真實的代理類存在,就是我們會代碼中創(chuàng)建一個代理類,并在代理類的方法中調(diào)用目標對象的方法,以此來完成代理的工作。動態(tài)代理的代理類沒有在代碼中創(chuàng)建一個代理類,而是在運行時在JVM里面創(chuàng)建代理對象。
靜態(tài)代理是有實實在在的代理類存在,并且和目標類實現(xiàn)相同的接口。
比如,有一個轉(zhuǎn)賬業(yè)務(wù),現(xiàn)在希望給它增加功能,使在轉(zhuǎn)賬之前確認轉(zhuǎn)賬人身份,以及轉(zhuǎn)賬之后通知收款人。
(1) 接口 AccountServiceDao :
package com.pingguo.spring5.dao; public interface AccountServiceDao { // 主業(yè)務(wù)邏輯,轉(zhuǎn)賬 void transfer(); }
(2) 接口 AccountServiceDao 的實現(xiàn)類:
package com.pingguo.spring5.dao; public class AccountServiceImpl implements AccountServiceDao { @Override public void transfer() { System.out.println("調(diào)用dao層,完成轉(zhuǎn)賬主業(yè)務(wù)."); } }
(3) 代理類 AccountProxy :
package com.pingguo.spring5.proxy; import com.pingguo.spring5.dao.AccountServiceDao; public class AccountProxy implements AccountServiceDao { // 目標對象 private AccountServiceDao target; public AccountProxy(AccountServiceDao target) { this.target = target; } /** * 代理方法,實現(xiàn)對目標方法的增強 */ @Override public void transfer() { before(); target.transfer(); after(); } /** * 增強的功能,轉(zhuǎn)賬之前使用 */ private void before() { System.out.println("對轉(zhuǎn)賬人身份進行驗證."); } /** * 增強的功能,轉(zhuǎn)賬之后使用 */ private void after() { System.out.println("轉(zhuǎn)賬完成,已通知收款人."); } }
在代理類中:
添加了添加了目標對象,并且有參構(gòu)造方法里需要傳入目標對象。
代理方法里,調(diào)用了目標對象里的轉(zhuǎn)賬方法 target.transfer()。
before() 和 after() 則是 2個增強的方法,分別作用于 target.transfer() 的前面和后面。
(4) 運行測試新建一個測試方法,運行看下結(jié)果:
@Test public void testProxy() { // 創(chuàng)建目標對象 AccountServiceDao target = new AccountServiceImpl(); // 創(chuàng)建代理對象 AccountProxy proxy = new AccountProxy(target); proxy.transfer(); }
這里先創(chuàng)建了目標對象
再創(chuàng)建代理對象,并且把目標對象傳入
最后調(diào)用代理對象里的,被增強過的方法 transfer()。
結(jié)果:
對轉(zhuǎn)賬人身份進行驗證. 調(diào)用dao層,完成轉(zhuǎn)賬主業(yè)務(wù). 轉(zhuǎn)賬完成,已通知收款人. Process finished with exit code 0
優(yōu)點:
效率高,因為所有的類都是已經(jīng)編寫完成的,使用的時候只需要取得代理對象并且執(zhí)行即可。
同時也可以實現(xiàn)對目標對象中指定的方法進行增強。
缺點:
與目標類實現(xiàn)相同的接口代碼,冗余。
如果接口發(fā)生改變,代理類中的方法也要修改。
代理類服務(wù)于一種類型的對象,如果要服務(wù)多類型的對象,那么要為每種類型的對象都生成代理類。
與靜態(tài)代理的硬編碼方式相比,動態(tài)代理支持運行時動態(tài)生成代理對象這種方式。換句話說,動態(tài)代理并不存在代理類,代理對象直接由代理生成工具動態(tài)生成。
優(yōu)點:
用很少的代碼對一個類的所有方法實現(xiàn)一樣的增強效果。
在編碼時,代理邏輯與業(yè)務(wù)邏輯互相獨立,各不影響,減少侵入,降低耦合。
缺點:
相對于靜態(tài)代理,它不能增強其中的某一個方法。
對于動態(tài)代理,針對于是否存在接口的情況下,又分為 2 種:
有接口的情況下,使用 JDK 動態(tài)代理。
無接口的情況下,使用 CGLIB 動態(tài)代理。
使用 JDK 動態(tài)代理
使用 JDK 動態(tài)代理,創(chuàng)建的是接口實現(xiàn)類的代理對象,以此來實現(xiàn)功能增強。
現(xiàn)在不需要上面創(chuàng)建過的實際代理類了 。
接口,為了后面的一些知識點的說明,里面加個參數(shù),轉(zhuǎn)賬的金額:
package com.pingguo.spring5.dao; public interface AccountServiceDao { // 主業(yè)務(wù)邏輯,轉(zhuǎn)賬 void transfer(int amount); }
實現(xiàn)類:
package com.pingguo.spring5.dao; public class AccountServiceImpl implements AccountServiceDao { @Override public void transfer(int amount) { System.out.println("調(diào)用dao層,完成轉(zhuǎn)賬主業(yè)務(wù).金額:" + amount); } }
在測試方法里,直接使用動態(tài)代理:
@Test public void testDynamicProxy() { // 創(chuàng)建目標對象 AccountServiceDao target = new AccountServiceImpl(); // 創(chuàng)建代理對象 AccountServiceDao proxy = (AccountServiceDao) Proxy.newProxyInstance( target.getClass().getClassLoader(), // 目標類使用的類加載器 target.getClass().getInterfaces(), // 目標類實現(xiàn)的接口 new InvocationHandler() { // 調(diào)用處理器 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("對轉(zhuǎn)賬人身份進行驗證."); Object res = method.invoke(target, args); System.out.println("轉(zhuǎn)賬完成,已通知收款人."); return res; } } ); // 讓代理工作 proxy.transfer(10000); }
運行結(jié)果:
對轉(zhuǎn)賬人身份進行驗證. 調(diào)用dao層,完成轉(zhuǎn)賬主業(yè)務(wù).金額:10000 轉(zhuǎn)賬完成,已通知收款人. Process finished with exit code 0
動態(tài)代理的過程:
創(chuàng)建處理器 InvocationHandler實例。
在調(diào)用目標對象時,會調(diào)用代理對象。
代理對象去請求目標對象。invoke 方法就是調(diào)用目標對象的方法生成代理對象的過程。
同時,在 invoke 方法中進行功能增強。
對于 invoke 中的 3 個參數(shù),分別是:
Object proxy:代理對象,一般不會使用。
Method method:外面的代理對象調(diào)用的方法引用,這里引用的就是 transfer()
Object[] args:外面的代理對象調(diào)用的方法里面的參數(shù),這里就是參數(shù) amount。
使用 CGLIB 動態(tài)代理
CGLIB動態(tài)代理的原理是生成目標類的子類,這個子類對象就是代理對象,代理對象是被增強過的。
注意,不管有沒有接口都可以使用 CGLIB 動態(tài)代理, 而不是只有在無接口的情況下才能使用。
到此,關(guān)于“Spring AOP底層原理及代理模式實例分析”的學習就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續(xù)學習更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。