Java 動態(tài)代理的多種實現(xiàn)方式
一、動態(tài)代理簡介
優(yōu)勢:在不修改源碼的情況下,對目標(biāo)方法進行相應(yīng)的增強。
作用:完成程序功能之間的松耦合。
二、動態(tài)代理的多種實現(xiàn)
- JDK代理:基于接口的動態(tài)代理技術(shù)(缺點,目標(biāo)對象必須有接口,如果沒有接口,則無法完成動態(tài)代理的實現(xiàn))
- cglib代理:基于父類的動態(tài)代理技術(shù)
兩者的區(qū)別如圖所示:

1. 基于JDK的實現(xiàn)
目標(biāo)接口類:
public interface TargetInterface {
public void save();
public void print(String str);
}
目標(biāo)類:
public class Target implements TargetInterface{
public void save() {
System.out.println("save running...");
}
public void print(String str) {
System.out.println(str);
}
}
增強類:
public class Advice {
public void before() {
System.out.println("前置增強");
}
public void after() {
System.out.println("后置增強");
}
}
測試類:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyTest {
public static void main(String[] args) {
//目標(biāo)對象
final Target target = new Target();
//增強對象
final Advice advice = new Advice();
TargetInterface proxyInstance = (TargetInterface)Proxy.newProxyInstance(
target.getClass().getClassLoader(), //目標(biāo)對象類加載器
target.getClass().getInterfaces(), //目標(biāo)對象相同的接口字節(jié)碼對象數(shù)組
new InvocationHandler() {
//調(diào)用代理對象的任何方法,實質(zhì)執(zhí)行的都是invoke方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
advice.before(); //前置增強
Object invoke = method.invoke(target, args); //執(zhí)行目標(biāo)方法
advice.after(); //后置增強
System.out.println();
return invoke;
}
});
//代理對象的方法測試
proxyInstance.save();
proxyInstance.print("JDK動態(tài)代理");
}
}
運行截圖:

2. 基于cglib的實現(xiàn)
需要導(dǎo)入Jar包,如果是maven項目,則在pom.xml文件加入如下配置:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.2.4.RELEASE</version> </dependency>
目標(biāo)類:
public class Target {
public void save() {
System.out.println("save running...");
}
public void print(String str) {
System.out.println(str);
}
}
增強類:
public class Advice {
public void before() {
System.out.println("前置增強");
}
public void after() {
System.out.println("后置增強");
}
}
測試類:
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
public class ProxyTest {
public static void main(String[] args) {
final Target target = new Target();
final Advice advice = new Advice();
//返回值就是動態(tài)生成的代理對象,基于cglib
//創(chuàng)建增強器
Enhancer enhancer = new Enhancer();
//設(shè)置父類(目標(biāo))
enhancer.setSuperclass(Target.class);
//設(shè)置回調(diào)
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object o, Method method, Object[] obj, MethodProxy methodProxy) throws Throwable{
advice.before();
Object invoke = method.invoke(target, obj);
advice.after();
System.out.println();
return invoke;
}
});
//創(chuàng)建代理對象
Target proxy = (Target)enhancer.create();
//測試代理方法
proxy.save();
proxy.print("基于cglib實現(xiàn)動態(tài)規(guī)劃");
}
}
運行截圖:

三、為什么要有基于cglib的實現(xiàn)
使用JDK動態(tài)代理實現(xiàn)時,最大限制是被增強對象必須實現(xiàn)接口,并且增強的方法只能是接口中聲明的方法。但在實際的項目中,可能總是存在對不實現(xiàn)業(yè)務(wù)接口的對象進行增強的需求,這時JDK動態(tài)代理將無能為力。
四、兩種方式的適用場景
JDK動態(tài)代理
優(yōu)點
- 不依賴第三方j(luò)ar包, 使用方便
- 隨著JDK的升級,JDK動態(tài)代理的性能在穩(wěn)步提升
缺點
- 只能代理實現(xiàn)了接口的類
- 執(zhí)行速度較慢
適用場景
- 如果你的程序需要頻繁、反復(fù)地創(chuàng)建代理對象,則JDK動態(tài)代理在性能上更占優(yōu)。
cglib
優(yōu)點
由于是動態(tài)生成字節(jié)碼實現(xiàn)代理,因此代理對象的執(zhí)行速度較快, 約為JDK動態(tài)代理的1.5 ~ 2倍
可以代理沒有實現(xiàn)接口的對象
缺點
- 不能代理final類
- 動態(tài)生成字節(jié)碼雖然執(zhí)行較快,但是生成速度很慢,根據(jù)網(wǎng)上一些人的測試結(jié)果,cglib創(chuàng)建代理對象的速度要比JDK慢10 ~ 15倍。
適用場景
- 不需要頻繁創(chuàng)建代理對象的應(yīng)用,如Spring中默認的單例bean,只需要在容器啟動時生成一次代理對象。
以上就是Java 動態(tài)代理的多種實現(xiàn)方式的詳細內(nèi)容,更多關(guān)于Java 動態(tài)代理的實現(xiàn)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
springboot2啟動時執(zhí)行,初始化(或定時任務(wù))servletContext問題
這篇文章主要介紹了springboot2啟動時執(zhí)行,初始化(或定時任務(wù))servletContext問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-01-01
SpringCloud負載均衡spring-cloud-starter-loadbalancer解讀
這篇文章主要介紹了SpringCloud負載均衡spring-cloud-starter-loadbalancer使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2025-03-03
Mybatis-Plus根據(jù)時間段去查詢數(shù)據(jù)的實現(xiàn)示例
這篇文章主要介紹了Mybatis-Plus根據(jù)時間段去查詢數(shù)據(jù)的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
Java JDBC連接數(shù)據(jù)庫常見操作總結(jié)
這篇文章主要介紹了Java JDBC連接數(shù)據(jù)庫常見操作,結(jié)合實例形式總結(jié)分析了java基于jdbc連接mysql、Oracle數(shù)據(jù)庫及連接池相關(guān)操作技巧,需要的朋友可以參考下2019-03-03
Java實現(xiàn)將byte[]轉(zhuǎn)換為File對象
這篇文章將通過一個簡單的例子為大家演示Java如何實現(xiàn) byte[] 轉(zhuǎn)換為 File 對象,并將其上傳到外部服務(wù)器,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-03-03
Java LinkedList的實現(xiàn)原理圖文詳解
今天小編就為大家分享一篇關(guān)于Java LinkedList的實現(xiàn)原理圖文詳解,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-01-01
MyBatis中動態(tài)sql的實現(xiàn)方法示例
這篇文章主要給大家介紹了關(guān)于MyBatis中動態(tài)sql的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-11-11

