java中的動態(tài)代理(jdk和Cglib)實現(xiàn)詳解
動態(tài)代理
一、JDK動態(tài)代理
準備
JDK動態(tài)代理只能代理接口,所以我們需要先準備一個接口,以及一個接口的實現(xiàn)類
// 需要代理的接口
public interface InvokeInterface {
void test1(String test,int[] ints);
String test3(String test);
}
//代理接口的實現(xiàn)類
public class InvokeBean implements InvokeInterface{
public void test1(String test,int[] ints){
System.out.println("代理實現(xiàn)方法1 :"+test + JSON.toJSON(ints));
}
public String test3(String test){
System.out.println("代理實現(xiàn)方法1 :"+test);
return test;
}
}有了代理的目標對象了,我們自然還需要一個處理方法,畢竟我們需要實現(xiàn)額外的邏輯呀,不然代理干嘛?對吧,這個處理方法可不能亂寫,需要實現(xiàn)InvocationHandler接口,里面有個invoke方法,就是給我們自己寫邏輯的地方,我們就可以對方法執(zhí)行前、后填充自己的邏輯了
注意: 這個類實例化的時候傳了一個Object參數(shù),這個就是上面的實現(xiàn)類,為什么要傳進來?因為一個接口是可以有多個實現(xiàn)類的,你不傳一個具體的實現(xiàn)類,我怎么知道要執(zhí)行哪個實現(xiàn)類里面的方法呢?
public class DemoInvokeHandler implements InvocationHandler {
private Object object;
public DemoInvokeHandler(Object object){
this.object=object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 所以可以在這里 方法執(zhí)行前寫邏輯
System.out.println("方法執(zhí)行前執(zhí)行");
// 這個就是我們原本的方法執(zhí)行
Object invoke=method.invoke(object,args);
// 所以可以在這里 方法執(zhí)行后寫邏輯
System.out.println("方法執(zhí)行后執(zhí)行");
return invoke;
}
}像以上這樣呢,整個接口中的方法都會被代理,如果我們只想代理某個方法呢?就需要對方法判斷一下如:
這里只是對方法名過濾了,還可以結合注解、參數(shù)等過濾
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(method.getName().equals("test1")){
// 所以可以在這里 方法執(zhí)行前寫邏輯
System.out.println("方法執(zhí)行前執(zhí)行");
// 這個就是我們原本的方法執(zhí)行
Object invoke=method.invoke(object,args);
// 所以可以在這里 方法執(zhí)行后寫邏輯
System.out.println("方法執(zhí)行后執(zhí)行");
return invoke;
}else {
return method.invoke(object,args);
}
}使用
以上我們的準備工作就完成了,我們只需要在實際使用中,獲取代理對象,執(zhí)行代理對象即可,通過
Proxy.newProxyInstance獲取代理對象,有三個傳參:
- 類加載器
- 被代理的接口
- 需要做的處理類(也就是實現(xiàn)了InvocationHandler的類)
// 獲取代理對象
InvokeInterface invokeBean = (InvokeInterface) Proxy.newProxyInstance(InvokeBean.class.getClassLoader(),
new Class[]{InvokeInterface.class}, new DemoInvokeHandler(new InvokeBean()));
invokeBean.test1("test1",new int[]{1,2});
String test3 = invokeBean.test3("test3");
System.out.println(test3);二、Cglib動態(tài)代理
準備
這個我們需要先導入依賴包
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.1</version>
</dependency>Cglib和JDK實現(xiàn)起來差不多,但是Cglib代理的是類,所以我們需要先準備個目標類
public class CglibTarget {
public void test(){
System.out.println("test 本方法執(zhí)行");
}
}同樣的還需要自己的處理方法,這個要實現(xiàn)的接口就不一樣。是Callback接口,而該接口有幾種:

我們只看一下MethodInterceptor和InvocationHandler兩種,其他畢竟用得少:
InvocationHandler:用法和JDK動態(tài)代理一樣,參考JDK的即可
MethodInterceptor:
注意: 是net.sf.cglib.proxy.MethodInterceptor包下的,同時這個處理類實例化的時候就不需要再傳參了
public class CglibProxy implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// 所以可以在這里 方法執(zhí)行前寫邏輯
System.out.println("方法執(zhí)行前執(zhí)行");
// 這個就是我們原本的方法執(zhí)行
Object o1 = methodProxy.invokeSuper(o, objects);
// 所以可以在這里 方法執(zhí)行后寫邏輯
System.out.println("方法執(zhí)行后執(zhí)行");
return o1;
}
}使用
Cglib的使用同樣是需要先生成一個代理對象,一般是靠Enhancer.create() 方法,但該方法有幾個重載,我們一個一個介紹
先是最簡單也是最常用的
Enhancer.create(Class type, Callback callback):
- type: 需要代理的類
- callback:處理方法類
// MethodInterceptor 方式 CglibTarget cglibTarget = (CglibTarget) Enhancer.create(CglibTarget.class, new CglibProxy()); cglibTarget.test(); // InvocationHandler 方式 與JDK一樣,不需要類加載器了 Interface cglibTarget = (Interface) Enhancer.create(Interface.class, new CglibProxy(new 實現(xiàn)類)); cglibTarget.test();
結果:

####Enhancer.create((Class superclass, Class[] interfaces, Callback callback)):
- superclass: 需要代理的類
- interfaces:需要實現(xiàn)的接口
- callback:處理方法類
這個就比上面那個更厲害了,上面那個只是代理原有類里面的方法,這個可以對代理原有的類幫他實現(xiàn)接口,并實現(xiàn)接口邏輯,這等于一個類憑空多了幾個方法出來,怎么用?
上述的目標類不需要改,處理方法需要改一下,然后新建一個接口:
// 我們新建一個接口不需要任何實現(xiàn)類
public interface TestTarget {
public void test1();
}
// 修改處理方法
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// 這里新增對上面接口里面方法的處理,其他的不需要變
if(method.getName().equals("test1")){
// 這里可以實現(xiàn)那個接口方法的邏輯
System.out.println("這里寫實現(xiàn)那個接口的邏輯方法");
return "我是那個接口返回的";
}else {
// 所以可以在這里 方法執(zhí)行前寫邏輯
System.out.println("方法執(zhí)行前執(zhí)行");
// 這個就是我們原本的方法執(zhí)行
Object o1 = methodProxy.invokeSuper(o, objects);
// 所以可以在這里 方法執(zhí)行后寫邏輯
System.out.println("方法執(zhí)行后執(zhí)行");
return o1;
}
}調用:
// 先用接口生成器 對我們要實現(xiàn)的接口做一個處理
InterfaceMaker interfaceMaker=new InterfaceMaker();
interfaceMaker.add(TestTarget.class);
Class aClass = interfaceMaker.create();
// 然后我們就可以正常的傳參調用了
CglibTarget o = (CglibTarget)new Enhancer().create(CglibTarget.class, new Class[]{aClass},new CglibProxy());
// 類中方法正常調用
o.test();
// 接口方法調用需要用反射 因為畢竟那接口的方法不存在對象方法里面
Method test1 = o.getClass().getMethod("test1");
test1.invoke(o);結果:

Enhancer. create(Class superclass, Class[] interfaces, CallbackFilter filter, Callback[] callbacks)
- superclass: 需要代理的類
- interfaces:需要實現(xiàn)的接口
- filter: 這個就是用來選擇處理類的,畢竟處理類有多個了
- callbacks:處理方法類數(shù)組(處理類可以有多個)
為了演示效果,我們新建一個處理類CglibProxyOther,上面的都不需要變
public class CglibProxyOther implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
if(method.getName().equals("test1")){
System.out.println("新建處理類的邏輯方法");
return "我是那個新建處理類返回的";
}else {
// 所以可以在這里 方法執(zhí)行前寫邏輯
System.out.println("新建處理類——方法執(zhí)行前執(zhí)行");
// 這個就是我們原本的方法執(zhí)行
Object o1 = methodProxy.invokeSuper(o, objects);
// 所以可以在這里 方法執(zhí)行后寫邏輯
System.out.println("新建處理類——方法執(zhí)行后執(zhí)行");
return o1;
}
}
}然后新增一個過濾選擇器filter:
實現(xiàn)CallbackFilter接口即可,返回值是處理類數(shù)組的下標(我這里用方面名稱來選擇,實際還可以用其他)
public class CglibFilter implements CallbackFilter {
@Override
public int accept(Method method) {
// test方法用下標為0的處理類
if(method.getName().equals("test")){
return 0;
}
// 其他方法用下標為1的處理類
return 1;
}
}調用:
InterfaceMaker interfaceMaker=new InterfaceMaker();
interfaceMaker.add(TestTarget.class);
Class aClass = interfaceMaker.create();
CglibTarget o = (CglibTarget)new Enhancer().create(CglibTarget.class, new Class[]{aClass},new CglibFilter(),new Callback[]{new CglibProxy(),new CglibProxyOther()});
o.test();
Method test1 = o.getClass().getMethod("test1");
test1.invoke(o);結果: 很明顯的看到test1方法走了第二個處理類

至此重載方法全介紹完畢!
三、總結對比
- 拓展性:要是考慮使用角度Cglib無疑是更好的,因為JDK只能代理接口
- 原理:JDK代理是利用反射機制生成匿名類,調用也是通過反射來調用 Cglib是采用字節(jié)碼技術,通過修改字節(jié)碼生成子類
- 效率:JDK創(chuàng)建對象效率較高,但執(zhí)行較慢,Cglib創(chuàng)建對象效率低,但執(zhí)行較快
- 局限性: Cglib需要額外導入第三方包,而Jdk代理不需要,但JDK局限于代理接口
到底用什么相信大家有選擇了,如果沒特殊需求,就直接JDK得了,有特殊需求不用Cglib,JDK能滿足嗎?至于效率,這種效率的差距都體現(xiàn)在一定量往上的程度,沒到這個量無需考慮太多!
到此這篇關于java中的動態(tài)代理(jdk和Cglib)實現(xiàn)詳解的文章就介紹到這了,更多相關java 動態(tài)代理內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
詳談HashMap和ConcurrentHashMap的區(qū)別(HashMap的底層源碼)
下面小編就為大家?guī)硪黄斦凥ashMap和ConcurrentHashMap的區(qū)別(HashMap的底層源碼)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08
Java基礎知識精通循環(huán)結構與break及continue
循環(huán)結構是指在程序中需要反復執(zhí)行某個功能而設置的一種程序結構。它由循環(huán)體中的條件,判斷繼續(xù)執(zhí)行某個功能還是退出循環(huán),選擇結構用于判斷給定的條件,根據(jù)判斷的結果判斷某些條件,根據(jù)判斷的結果來控制程序的流程2022-04-04
java并發(fā)編程專題(八)----(JUC)實例講解CountDownLatch
這篇文章主要介紹了java CountDownLatch的相關資料,文中示例代碼非常詳細,幫助大家理解和學習,感興趣的朋友可以了解下2020-07-07
Spring中的SpringApplicationRunListener詳細解析
這篇文章主要介紹了Spring中的SpringApplicationRunListener詳細解析,SpringApplicationRunListener是一個監(jiān)聽SpringApplication中run方法的接口,在項目啟動過程的各個階段進行事件的發(fā)布,需要的朋友可以參考下2023-11-11

