1:什么是代理模式
代理模式是一種比較好理解的設(shè)計模式。
我們使用代理對象來代替真實對象的訪問,這樣子就可以在不修改原目標(biāo)對象的前提下,
提供額外的功能操作,擴展目標(biāo)對象的功能。
2:靜態(tài)代理
實際應(yīng)用場景非常少
靜態(tài)代理中:我們對目標(biāo)對象的每個方法的增強都是手動完成的
如:某個接口一旦新增了方法,代理對象和目標(biāo)對象都要進(jìn)行修改
從JVM層面上:靜態(tài)代理就是在編譯時將接口、實現(xiàn)類、代理類變成一個個class文件,
1:定義一個接口和其實現(xiàn)類
2:創(chuàng)建一個代理類同樣實現(xiàn)這個接口
3:將目標(biāo)對象注入到代理類,然后在代理類的對應(yīng)方法 調(diào)用目標(biāo)類中的方法。
我們就可以通過代理類屏蔽對目標(biāo)對象的訪問,并且可以在目標(biāo)方法執(zhí)行前后做一些自己的事情
3:動態(tài)代理
相對于靜態(tài)代理來說,動態(tài)代理更加靈活。
我們不需要針對每個目標(biāo)都獨立創(chuàng)建一個個代理類,并且也不需要我們必須實現(xiàn)接口;
從JVM角度來說,動態(tài)代碼在運行時動態(tài)生成類字節(jié)碼,并加重到JVM中的。
其中Spring AOP 和 RPC框架的實際都是依賴離開動態(tài)代理;
動態(tài)代理又分 JDK動態(tài)代理 和 CGLIB動態(tài)代理
4:JDK動態(tài)代理
Spring AOP 中默認(rèn)的代理方式 注意:只能代理實現(xiàn)了接口的類
具體調(diào)用流程:
在通過Proxy類的 newProxyInstance()創(chuàng)建的代理對象調(diào)用方法的時候,
實際會調(diào)用到實現(xiàn)InvocationHandler類的 invoke()方法。
可以在 invoke()方法自定義處理邏輯。
具體實現(xiàn)流程
1:定義一個接口及其實現(xiàn)類;
2:通過 Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 創(chuàng)建代理對象
3:自定義InvocationHandler 并重寫 invoke方法,
在invoke方法中調(diào)用被代理類的的方法并自定義一些處理邏輯。
Proxy類中使用頻率最高的方法是:newProxyInstance() 主要用于生成一個代理對象
newProxyInstance方法中的三個參數(shù)
1:loader:類加載器 用于加載代理對象;
2:interfaces:被代理的一些接口;
3:h:實現(xiàn)了InvocationHandler 接口的對象;
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
throws IllegalArgumentException
{ ...... }
自定義InvocationHandler類中的 invoke 為反射方法
invoke方法中的三個參數(shù)
1:proxy:動態(tài)生成的代理類
2:method:與代理類對象調(diào)用的方法相對應(yīng)
3:args:當(dāng)前method方法的參數(shù)
public interface InvocationHandler {
/** * 當(dāng)你使用代理對象調(diào)用方法的時候?qū)嶋H會調(diào)用到這個方法 */
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
5:CFLIB動態(tài)代理
JDK動態(tài)代理有一個致命的問題:只能代理實現(xiàn)了接口的類
GCLIB通過繼承的方式實現(xiàn)代理,
例如在Spring中的AOP模塊中:如果目標(biāo)對象實現(xiàn)了接口,則默認(rèn)采用JDK動態(tài)代理,否則使用CGLIB動態(tài)代理。
具體調(diào)用流程:
在通過Enhancer類的 create()創(chuàng)建的代理對象調(diào)用方法的時候,
實際會調(diào)用到實現(xiàn)MethodInterceptor 類的intercept()方法。
可以在 intercept()方法自定義處理邏輯。
具體實現(xiàn)流程
1:定義一個類;
2:通過 Enhancer 類的create() 創(chuàng)建代理類
3:自定義MethodInterceptor 并重寫 intercept方法,
在intercept方法中調(diào)用被代理類的的方法并自定義一些處理邏輯。
Enhancer類 為cglib.proxy.Enhancer 該包下的 動態(tài)代理增強類
// 創(chuàng)建動態(tài)代理增強類
Enhancer enhancer = new Enhancer();
// 設(shè)置類加載器
enhancer.setClassLoader(clazz.getClassLoader());/
// 設(shè)置被代理類
enhancer.setSuperclass(clazz);
// 設(shè)置方法攔截器
enhancer.setCallback(new MyMethodInterceptor());
// 創(chuàng)建代理類
return enhancer.create();
自定義MyMethodInterceptor 類中的 intercept 方法
intercept 方法中的四個參數(shù):
1:obj : 被代理的對象(需要增強的對象)
2:method : 被攔截的方法(需要增強的方法)
3:objects : 方法入?yún)? 4:methodProxy :用于調(diào)用原始方法
public interface MethodInterceptor extends Callback{
// 攔截被代理類中的方法
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args, MethodProxy proxy)
throws Throwable;
}
6:靜態(tài)代理和動態(tài)代理的區(qū)別總結(jié)
靈活性:
靜態(tài)代理中接口一旦新增加方法,目標(biāo)對象和代理對象都要進(jìn)行修改,這是非常麻煩的;
動態(tài)代理更加靈活,可以直接代理實現(xiàn)類,并不需要針對每一個目標(biāo)類都創(chuàng)建一個代理類。
JVM層面:
靜態(tài)代理在編譯時就將接口、實現(xiàn)類、代理類這些變成一個個實際的class文件
動態(tài)代理在運行期動態(tài)生成類字節(jié)碼 并 加載到JVM中
7:JDK動態(tài)代理 和 CGLib動態(tài)代理 區(qū)別
1:JDK動態(tài)代理是?帶的,CGlib需要引?第三?包
2:JDK動態(tài)代理,要求?標(biāo)對象實現(xiàn)?個接?,
但是有時候?標(biāo)對象只是?個單獨的對象,并沒有實現(xiàn)任何的接?,這個時候就可以?CGLib動態(tài)代理
3:CGLib動態(tài)代理,它是在內(nèi)存中構(gòu)建?個?類對象從?實現(xiàn)對?標(biāo)對象功能的擴展
CGLib動態(tài)代理基于繼承來實現(xiàn)代理,所以?法對final類、private?法和static?法實現(xiàn)代理
Spring AOP中的代理使?的默認(rèn)策略?
如果?標(biāo)對象實現(xiàn)了接?,則默認(rèn)采?JDK動態(tài)代理
如果?標(biāo)對象沒有實現(xiàn)接?,則采?CgLib進(jìn)?動態(tài)代理
如果?標(biāo)對象實現(xiàn)了接?,程序??依舊可以指定使?CGlib動態(tài)代理
項目連接
請配合項目代碼食用效果更佳:
項目地址:
https://github.com/hesuijin/rpc-project
Git下載地址:
https://github.com.cnpmjs.org/hesuijin/rpc-project.git
rpc-project-demo模塊下 proxyDemo包