java中動態(tài)代理如何實現(xiàn)詳解
Java實現(xiàn)動態(tài)代理的兩種方式
- JDK動態(tài)代理:Java.lang.reflect 包中的Proxy類和InvocationHandler接口提供了生成動態(tài)代理類的能力。
- Cglib動態(tài)代理:Cglib (Code Generation Library )是一個第三方代碼生成類庫,運行時在內(nèi)存中動態(tài)生成一個子類對象從而實現(xiàn)對目標對象功能的擴展。
兩種動態(tài)代理的區(qū)別
JDK 的動態(tài)代理是基于接口的代理。
它要求被代理的類必須實現(xiàn)一個或多個接口。在運行時,JDK 動態(tài)代理會根據(jù)被代理類實現(xiàn)的接口生成一個代理對象,該代理對象實現(xiàn)了被代理類的接口,并將方法的調(diào)用轉(zhuǎn)發(fā)給真正的被代理類。JDK 動態(tài)代理的優(yōu)點是簡單易用,缺點是只能代理實現(xiàn)了接口的類。
CGLIB 是基于繼承的代理。
它可以代理沒有實現(xiàn)任何接口的類。在運行時,CGLIB 會動態(tài)生成一個被代理類的子類(Cglib包的底層是通過使用一個小而快的字節(jié)碼處理框架ASM,來轉(zhuǎn)換字節(jié)碼并生成新的類。不鼓勵直接使用ASM,因為它需要你對JVM內(nèi)部結(jié)構(gòu)包括class文件的格式和指令集都很熟悉。),并重寫父類中的方法,從而實現(xiàn)代理功能。CGLIB 的優(yōu)點是可以代理沒有實現(xiàn)接口的類,缺點是生成的代理類需要繼承被代理類,并且無法代理 final 類型的方法。它可以在運行期擴展Java類與實現(xiàn)Java接口。它廣泛的被許多AOP的框架使用,例如Spring AOP和dynaop,為他們提供方法的interception(攔截)。
總結(jié)
使用JDK動態(tài)代理的對象必須實現(xiàn)一個或多個接口;而使用cglib代理的對象則無需實現(xiàn)接口,達到代理類無侵入。
JDK 的動態(tài)代理和 CGLIB 都有各自的優(yōu)點和缺點,具體使用哪種方式取決于具體的需求和場景。如果被代理的類已經(jīng)實現(xiàn)了接口,那么可以優(yōu)先考慮使用 JDK 的動態(tài)代理;如果被代理的類沒有實現(xiàn)接口,或者需要對類的所有方法進行代理,那么可以考慮使用 CGLIB。
補充
靜態(tài)代理和動態(tài)代理的區(qū)別
最大的區(qū)別就是靜態(tài)代理是編譯期確定的,但是動態(tài)代理卻是運行期確定的。
同時,使用靜態(tài)代理模式需要程序員手寫很多代碼,這個過程是比較浪費時間和精力的。一旦需要代理的類中方法比較多,或者需要同時代理多個對象的時候,這無疑會增加很大的復(fù)雜度。
反射是動態(tài)代理的實現(xiàn)方式之一。
動態(tài)代理的用途
Java的動態(tài)代理,在日常開發(fā)中可能并不經(jīng)常使用,但是并不代表他不重要。Java的動態(tài)代理的最主要的用途就是應(yīng)用在各種框架中。因為使用動態(tài)代理可以很方便的運行期生成代理類,通過代理類可以做很多事情,比如AOP,比如過濾器、攔截器等。
在我們平時使用的框架中,像servlet的filter、包括spring提供的aop以及struts2的攔截器都使用了動態(tài)代理功能。我們?nèi)粘?吹降膍ybatis分頁插件,以及日志攔截、事務(wù)攔截、權(quán)限攔截這些幾乎全部由動態(tài)代理的身影。
Spring AOP的實現(xiàn)方式
Spring AOP中的動態(tài)代理主要有兩種方式,JDK動態(tài)代理和CGLIB動態(tài)代理。
JDK動態(tài)代理通過反射來接收被代理的類,并且要求被代理的類必須實現(xiàn)一個接口。JDK動態(tài)代理的核心是InvocationHandler接口和Proxy類。
如果目標類沒有實現(xiàn)接口,那么Spring AOP會選擇使用CGLIB來動態(tài)代理目標類。
CGLIB(Code Generation Library),是一個代碼生成的類庫,可以在運行時動態(tài)的生成某個類的子類,注意,CGLIB是通過繼承的方式做的動態(tài)代理,因此如果某個類被標記為final,那么它是無法使用CGLIB做動態(tài)代理的。
JDK 動態(tài)代理代碼示例
public class UserServiceImpl implements UserService {
@Override
public void add() {
// TODO Auto-generated method stub
System.out.println("--------------------add----------------------");
}
}
public class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
super();
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在方法調(diào)用前進行性能監(jiān)控
PerformanceMonior.begin(target.getClass().getName()+"."+method.getName());
// 通過反射調(diào)用目標對象的方法
Object result = method.invoke(target, args);
// 在方法調(diào)用后結(jié)束性能監(jiān)控
PerformanceMonior.end();
return result;
}
public Object getProxy(){
return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass().getInterfaces(), this);
}
}
public static void main(String[] args) {
UserService service = new UserServiceImpl();
MyInvocationHandler handler = new MyInvocationHandler(service);
UserService proxy = (UserService) handler.getProxy();
proxy.add();
}
代碼整體解讀:
說人話就是我們通過jdk的動態(tài)代理,對用戶服務(wù)的新增用戶方法,追加了一個性能監(jiān)控功能,通過傳入原對象,得到代理對象(傳入原對象,我攔截開啟性能監(jiān)控功能,我再把方法的調(diào)用轉(zhuǎn)發(fā)(本質(zhì)就是反射機制調(diào)用)給原對象,原對象方法執(zhí)行結(jié)束,我結(jié)束性能監(jiān)控)。
Cglib動態(tài)代理代碼示例
public class UserServiceImpl implements UserService {
@Override
public void add() {
// TODO Auto-generated method stub
System.out.println("--------------------add----------------------");
}
}
public class CglibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz) {
// 設(shè)置需要創(chuàng)建子類的類
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
// 通過字節(jié)碼技術(shù)動態(tài)創(chuàng)建子類實例
return enhancer.create();
}
// 實現(xiàn)MethodInterceptor接口方法
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("前置代理");
// 通過代理類調(diào)用父類中的方法
Object result = proxy.invokeSuper(obj, args);
System.out.println("后置代理");
return result;
}
}
public class DoCGLib {
public static void main(String[] args) {
CglibProxy proxy = new CglibProxy();
// 通過生成子類的方式創(chuàng)建代理類
UserServiceImpl proxyImp = (UserServiceImpl)proxy.getProxy(UserServiceImpl.class);
proxyImp.add();
}
}總結(jié)
到此這篇關(guān)于java中動態(tài)代理如何實現(xiàn)的文章就介紹到這了,更多相關(guān)java動態(tài)代理實現(xiàn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java 直接調(diào)用python腳本,并傳遞參數(shù)代碼實例
這篇文章主要介紹了java調(diào)用python腳本傳遞參數(shù)的方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
Spring boot 項目中如何進行kafka Stream app 
文章介紹了Kafka Streams的配置要點與核心方法,強調(diào)應(yīng)用ID唯一性、正確設(shè)置Bootstrap服務(wù)器,區(qū)分KStream與Java Stream的不可變性和多消費特性,本文給大家介紹Springboot項目中如何進行kafka Stream app開發(fā),感興趣的朋友一起看看吧2025-09-09
java使用elasticsearch分組進行聚合查詢過程解析
這篇文章主要介紹了java使用elasticsearch分組進行聚合查詢過程解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-02-02
Spring cloud Gateway簡介及相關(guān)配置方法
這篇文章主要介紹了Spring cloud Gateway簡介及相關(guān)配置方法,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-04-04
JavaIO?BufferedReader和BufferedWriter使用及說明
這篇文章主要介紹了JavaIO?BufferedReader和BufferedWriter使用及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-12-12

