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

我們只看一下MethodInterceptor和InvocationHandler兩種,其他畢竟用得少:
InvocationHandler:用法和JDK動(dòng)態(tài)代理一樣,參考JDK的即可
MethodInterceptor:
注意: 是net.sf.cglib.proxy.MethodInterceptor包下的,同時(shí)這個(gè)處理類實(shí)例化的時(shí)候就不需要再傳參了
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í)行");
// 這個(gè)就是我們原本的方法執(zhí)行
Object o1 = methodProxy.invokeSuper(o, objects);
// 所以可以在這里 方法執(zhí)行后寫邏輯
System.out.println("方法執(zhí)行后執(zhí)行");
return o1;
}
}使用
Cglib的使用同樣是需要先生成一個(gè)代理對象,一般是靠Enhancer.create() 方法,但該方法有幾個(gè)重載,我們一個(gè)一個(gè)介紹
先是最簡單也是最常用的
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 實(shí)現(xiàn)類)); cglibTarget.test();
結(jié)果:

####Enhancer.create((Class superclass, Class[] interfaces, Callback callback)):
- superclass: 需要代理的類
- interfaces:需要實(shí)現(xiàn)的接口
- callback:處理方法類
這個(gè)就比上面那個(gè)更厲害了,上面那個(gè)只是代理原有類里面的方法,這個(gè)可以對代理原有的類幫他實(shí)現(xiàn)接口,并實(shí)現(xiàn)接口邏輯,這等于一個(gè)類憑空多了幾個(gè)方法出來,怎么用?
上述的目標(biāo)類不需要改,處理方法需要改一下,然后新建一個(gè)接口:
// 我們新建一個(gè)接口不需要任何實(shí)現(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")){
// 這里可以實(shí)現(xiàn)那個(gè)接口方法的邏輯
System.out.println("這里寫實(shí)現(xiàn)那個(gè)接口的邏輯方法");
return "我是那個(gè)接口返回的";
}else {
// 所以可以在這里 方法執(zhí)行前寫邏輯
System.out.println("方法執(zhí)行前執(zhí)行");
// 這個(gè)就是我們原本的方法執(zhí)行
Object o1 = methodProxy.invokeSuper(o, objects);
// 所以可以在這里 方法執(zhí)行后寫邏輯
System.out.println("方法執(zhí)行后執(zhí)行");
return o1;
}
}調(diào)用:
// 先用接口生成器 對我們要實(shí)現(xiàn)的接口做一個(gè)處理
InterfaceMaker interfaceMaker=new InterfaceMaker();
interfaceMaker.add(TestTarget.class);
Class aClass = interfaceMaker.create();
// 然后我們就可以正常的傳參調(diào)用了
CglibTarget o = (CglibTarget)new Enhancer().create(CglibTarget.class, new Class[]{aClass},new CglibProxy());
// 類中方法正常調(diào)用
o.test();
// 接口方法調(diào)用需要用反射 因?yàn)楫吘鼓墙涌诘姆椒ú淮嬖趯ο蠓椒ɡ锩?
Method test1 = o.getClass().getMethod("test1");
test1.invoke(o);結(jié)果:

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

至此重載方法全介紹完畢!
三、總結(jié)對比
- 拓展性:要是考慮使用角度Cglib無疑是更好的,因?yàn)镴DK只能代理接口
- 原理:JDK代理是利用反射機(jī)制生成匿名類,調(diào)用也是通過反射來調(diào)用 Cglib是采用字節(jié)碼技術(shù),通過修改字節(jié)碼生成子類
- 效率:JDK創(chuàng)建對象效率較高,但執(zhí)行較慢,Cglib創(chuàng)建對象效率低,但執(zhí)行較快
- 局限性: Cglib需要額外導(dǎo)入第三方包,而Jdk代理不需要,但JDK局限于代理接口
到底用什么相信大家有選擇了,如果沒特殊需求,就直接JDK得了,有特殊需求不用Cglib,JDK能滿足嗎?至于效率,這種效率的差距都體現(xiàn)在一定量往上的程度,沒到這個(gè)量無需考慮太多!
到此這篇關(guān)于java中的動(dòng)態(tài)代理(jdk和Cglib)實(shí)現(xiàn)詳解的文章就介紹到這了,更多相關(guān)java 動(dòng)態(tài)代理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java兩種動(dòng)態(tài)代理JDK動(dòng)態(tài)代理和CGLIB動(dòng)態(tài)代理詳解
- Java JDK與cglib動(dòng)態(tài)代理有什么區(qū)別
- Java反射(JDK)與動(dòng)態(tài)代理(CGLIB)詳解
- Java使用JDK與Cglib動(dòng)態(tài)代理技術(shù)統(tǒng)一管理日志記錄
- java 中動(dòng)態(tài)代理(JDK,cglib)實(shí)例代碼
- 深入理解java動(dòng)態(tài)代理的兩種實(shí)現(xiàn)方式(JDK/Cglib)
- 淺談Java代理(jdk靜態(tài)代理、動(dòng)態(tài)代理和cglib動(dòng)態(tài)代理)
- java動(dòng)態(tài)代理(jdk與cglib)詳細(xì)解析
相關(guān)文章
IDEA配置和啟動(dòng)maven項(xiàng)目詳細(xì)步驟
本文介紹了從SVN檢出Web項(xiàng)目并進(jìn)行Maven化、JDK和項(xiàng)目結(jié)構(gòu)配置、Spring和Tomcat環(huán)境搭建的詳細(xì)步驟,幫助讀者順利完成Java Web項(xiàng)目的開發(fā)環(huán)境搭建2025-10-10
idea設(shè)置回車換行縮進(jìn)空格數(shù)為4的實(shí)現(xiàn)方式
文章主要講述了解決在設(shè)置里回車換行僅縮進(jìn)兩空格的問題,通過將源代碼設(shè)置從回車兩空格改為回車四空格,解決了作者習(xí)慣上的不適2026-04-04
Java中將MultipartFile和File互轉(zhuǎn)的方法詳解
我們在開發(fā)過程中經(jīng)常需要接收前端傳來的文件,通常需要處理MultipartFile格式的文件,今天來介紹一下MultipartFile和File怎么進(jìn)行優(yōu)雅的互轉(zhuǎn),需要的朋友可以參考下2023-10-10
Java 生產(chǎn)者/消費(fèi)者問題實(shí)例詳解
這篇文章主要實(shí)例分析了java中生產(chǎn)者消費(fèi)者問題的方法,需要的朋友可以可以參考下2017-04-04
自定義Spring Security的身份驗(yàn)證失敗處理方法
在本篇文章里小編給大家整理了一篇關(guān)于自定義Spring Security的身份驗(yàn)證失敗的處理方法,有需要的朋友們學(xué)習(xí)下。2019-05-05
Java 其中翻轉(zhuǎn)字符串的實(shí)現(xiàn)方法
這篇文章主要介紹了Java 其中翻轉(zhuǎn)字符串的實(shí)現(xiàn)方法,需要的朋友可以參考下2014-02-02

