Spring AOP的底層實現(xiàn)方式-代理模式
在學(xué)習(xí)Spring的過程中,留下一下痕跡。
代理模式,其實就是讓別人做同樣的事情,但是別人卻不僅將我的事情做了,還會把他的事情也做了,換言之,這個人做的事情,是他獨有的事情和我需要做的事情的綜合?;氐酱a,那么就是,代理類執(zhí)行與委托類同樣的方法,在這方法里代理類不僅可以執(zhí)行委托類的方法的內(nèi)容,還可以添加自己的另外的內(nèi)容,在使用代理類的時候,會比直接使用委托類具有更多的能力,所以我們會直接使用代理類。
通常,代理類雖然具備更多的能力,但是代理類更多的能力其實是比較固定的。
例如,在JDBC里,或在ORM框架里,都會存在事務(wù)的開啟和提交,如果我們直接將事務(wù)代碼和業(yè)務(wù)代碼寫在一起,其實也是可以的,不過,這樣做會使得每一個需要事務(wù)的方法,都要添加事務(wù)代碼,造成代碼的分散冗余,那么,針對這種情況,我們希望業(yè)務(wù)代碼可以抽離出來,每一個業(yè)務(wù)方法都只寫業(yè)務(wù)內(nèi)容,同時我們又希望,在執(zhí)行業(yè)務(wù)方法時,可以有一種方式自動添加業(yè)務(wù)代碼,這種需求,其實就是有了代理的需求,因為我們希望還是使用原來的方法,但是出來的效果希望是多出事務(wù)代碼,也就是希望業(yè)務(wù)代碼得到增強(qiáng)??梢赃@樣理解了,抽離出來的業(yè)務(wù)代碼,是一個委托類,而可以將委托類自動增強(qiáng)并添加事務(wù)代碼的代碼,是一個代理類,它代理執(zhí)行委托類,具備了委托類原有的業(yè)務(wù)能力之余,增加了事務(wù)處理的代碼。
我理解的代理模式,其實就是將委托類融入到代理類里,換句話說,代理類也就是委托類的擴(kuò)充而已。
還有不僅是spring 的AOP是代理模式,還有Struts2的攔截器實現(xiàn)也是基于動態(tài)代理的,動態(tài)代理是一種很常見也很重要的設(shè)計模式。
說了很多,那么如何實現(xiàn)代理,那我們先說說靜態(tài)代理。
1.1 靜態(tài)代理
一個接口,兩個實現(xiàn)類,代理實現(xiàn)類組合真實實現(xiàn)類
靜態(tài)代理,是一種根據(jù)上面的理論,很自然會想到的一種不依賴于其他技術(shù)的代理模式實現(xiàn)方式。而他的實現(xiàn)過程如下圖。
如果使用過靜態(tài)代理,那么很容易理解,靜態(tài)代理存在的缺陷。
因此,也就出現(xiàn)了動態(tài)代理。
動態(tài)代理的動態(tài), 就是可以動態(tài)的切換真實實現(xiàn)類, 也就是說可以一個代理類(相同的代碼, 相同的增強(qiáng)操作)應(yīng)對一堆不確定的真實實現(xiàn)類.
1.2 動態(tài)代理
JDK動態(tài)代理和CGlib字節(jié)碼動態(tài)代理
1.2.1 JDK動態(tài)代理(必須有接口)
通過java.lang.reflect.Proxy類實現(xiàn)。
動態(tài)代理就是為了解決靜態(tài)代理不靈活的缺陷而產(chǎn)生的。靜態(tài)代理是固定的,一旦確定了代碼,如果委托類新增一個方法,而這個方法又需要增強(qiáng),那么就必須在代理類里重寫一個帶增強(qiáng)的方法。而動態(tài)代理可以靈活替換代理方法,動態(tài)就是體現(xiàn)在這里。
public interface TargetClass { void sayHello(); }
public class TargetClassImpl implements TargetClass{ public void sayHello() { System.out.println("你好"); } }
public class JdkProxy implements InvocationHandler { private TargetClass targetClass; public Object createProxy(TargetClass targetClass){ //傳入真實實現(xiàn)類, 本身要做的事情會由他自己做, 代理類會額外進(jìn)行其他增強(qiáng)操作 this.targetClass = targetClass; //獲取本類類加載器 ClassLoader classLoader = JdkProxy.class.getClassLoader(); ///獲取被代理對象的所有接口 Class[] clazz = targetClass.getClass().getInterfaces(); System.out.println(clazz.length); //生成代理類并返回 return Proxy.newProxyInstance(classLoader, clazz, this); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("JDKProxy前置增強(qiáng)"); Object obj = method.invoke(targetClass,args); System.out.println("JDKProxy后置增強(qiáng)"); return obj; } }
public class Test { public static void main(String[] args) { JdkProxy jdkProxy = new JdkProxy(); TargetClass targetClass = new TargetClassImpl(); TargetClass targetClass1 = (TargetClass) jdkProxy.createProxy(targetClass); targetClass1.sayHello(); }
1.2.2 CGlib動態(tài)代理
(不需要類繼承任何接口,字節(jié)碼技術(shù))
CGlib包在Spring core包里。
public class CGlibTaretClass { public void sayHello(){ System.out.println("我是CGlib,我不需要接口"); } }
package CGlibProxyTest; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class CGlibProxy implements MethodInterceptor{ //代理方法 public Object createProxy(Object target){ //創(chuàng)建一個動態(tài)類對象 Enhancer enhancer = new Enhancer(); //確定要增強(qiáng)的類,設(shè)置期父類 enhancer.setSuperclass(target.getClass()); //添加回調(diào)函數(shù) enhancer.setCallback(this); //返回創(chuàng)建的代理類 return enhancer.create(); } public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("CGlib前置增強(qiáng)"); Object obj = methodProxy.invokeSuper(o,objects); System.out.println("CGlib后置增強(qiáng)"); return obj; } }
public class Test { public static void main(String[] args) { CGlibProxy cGlibProxy = new CGlibProxy(); CGlibTaretClass cGlibTaretClass = new CGlibTaretClass(); CGlibTaretClass cGlibTaretClass1 = (CGlibTaretClass)cGlibProxy.createProxy(cGlibTaretClass); cGlibTaretClass1.sayHello(); }
最后:代理模式解決的是常見的代碼組織問題,它并不神秘,不要被他的名字嚇倒。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringCloud實現(xiàn)文件上傳功能的方法詳解
這篇文章主要為大家詳細(xì)介紹了SpringCloud如何實現(xiàn)文件上傳功能,文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)有一定的借鑒價值,需要的可以參考一下2022-08-08基于maven搭建一個ssm的web項目的詳細(xì)圖文教程
這篇文章主要介紹了基于maven搭建一個ssm的web項目的詳細(xì)教程,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-09-09springboot logback如何從apollo配置中心讀取變量
這篇文章主要介紹了springboot logback如何從apollo配置中心讀取變量的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08idea中使用SonarLint進(jìn)行代碼規(guī)范檢測及使用方法
這篇文章主要介紹了idea中使用SonarLint進(jìn)行代碼規(guī)范檢測,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-08-08淺析Java類和數(shù)據(jù)結(jié)構(gòu)中常用的方法
下面小編就為大家?guī)硪黄獪\析Java類和數(shù)據(jù)結(jié)構(gòu)中常用的方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-09-09