Java 動(dòng)態(tài)代理的多種實(shí)現(xiàn)方式
一、動(dòng)態(tài)代理簡(jiǎn)介
優(yōu)勢(shì):在不修改源碼的情況下,對(duì)目標(biāo)方法進(jìn)行相應(yīng)的增強(qiáng)。
作用:完成程序功能之間的松耦合。
二、動(dòng)態(tài)代理的多種實(shí)現(xiàn)
- JDK代理:基于接口的動(dòng)態(tài)代理技術(shù)(缺點(diǎn),目標(biāo)對(duì)象必須有接口,如果沒有接口,則無(wú)法完成動(dòng)態(tài)代理的實(shí)現(xiàn))
- cglib代理:基于父類的動(dòng)態(tài)代理技術(shù)
兩者的區(qū)別如圖所示:
1. 基于JDK的實(shí)現(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); } }
增強(qiáng)類:
public class Advice { public void before() { System.out.println("前置增強(qiáng)"); } public void after() { System.out.println("后置增強(qiáng)"); } }
測(cè)試類:
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)對(duì)象 final Target target = new Target(); //增強(qiáng)對(duì)象 final Advice advice = new Advice(); TargetInterface proxyInstance = (TargetInterface)Proxy.newProxyInstance( target.getClass().getClassLoader(), //目標(biāo)對(duì)象類加載器 target.getClass().getInterfaces(), //目標(biāo)對(duì)象相同的接口字節(jié)碼對(duì)象數(shù)組 new InvocationHandler() { //調(diào)用代理對(duì)象的任何方法,實(shí)質(zhì)執(zhí)行的都是invoke方法 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{ advice.before(); //前置增強(qiáng) Object invoke = method.invoke(target, args); //執(zhí)行目標(biāo)方法 advice.after(); //后置增強(qiáng) System.out.println(); return invoke; } }); //代理對(duì)象的方法測(cè)試 proxyInstance.save(); proxyInstance.print("JDK動(dòng)態(tài)代理"); } }
運(yùn)行截圖:
2. 基于cglib的實(shí)現(xiàn)
需要導(dǎo)入Jar包,如果是maven項(xiàng)目,則在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); } }
增強(qiáng)類:
public class Advice { public void before() { System.out.println("前置增強(qiáng)"); } public void after() { System.out.println("后置增強(qiáng)"); } }
測(cè)試類:
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(); //返回值就是動(dòng)態(tài)生成的代理對(duì)象,基于cglib //創(chuàng)建增強(qiá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)建代理對(duì)象 Target proxy = (Target)enhancer.create(); //測(cè)試代理方法 proxy.save(); proxy.print("基于cglib實(shí)現(xiàn)動(dòng)態(tài)規(guī)劃"); } }
運(yùn)行截圖:
三、為什么要有基于cglib的實(shí)現(xiàn)
使用JDK動(dòng)態(tài)代理實(shí)現(xiàn)時(shí),最大限制是被增強(qiáng)對(duì)象必須實(shí)現(xiàn)接口,并且增強(qiáng)的方法只能是接口中聲明的方法。但在實(shí)際的項(xiàng)目中,可能總是存在對(duì)不實(shí)現(xiàn)業(yè)務(wù)接口的對(duì)象進(jìn)行增強(qiáng)的需求,這時(shí)JDK動(dòng)態(tài)代理將無(wú)能為力。
四、兩種方式的適用場(chǎng)景
JDK動(dòng)態(tài)代理
優(yōu)點(diǎn)
- 不依賴第三方j(luò)ar包, 使用方便
- 隨著JDK的升級(jí),JDK動(dòng)態(tài)代理的性能在穩(wěn)步提升
缺點(diǎn)
- 只能代理實(shí)現(xiàn)了接口的類
- 執(zhí)行速度較慢
適用場(chǎng)景
- 如果你的程序需要頻繁、反復(fù)地創(chuàng)建代理對(duì)象,則JDK動(dòng)態(tài)代理在性能上更占優(yōu)。
cglib
優(yōu)點(diǎn)
由于是動(dòng)態(tài)生成字節(jié)碼實(shí)現(xiàn)代理,因此代理對(duì)象的執(zhí)行速度較快, 約為JDK動(dòng)態(tài)代理的1.5 ~ 2倍
可以代理沒有實(shí)現(xiàn)接口的對(duì)象
缺點(diǎn)
- 不能代理final類
- 動(dòng)態(tài)生成字節(jié)碼雖然執(zhí)行較快,但是生成速度很慢,根據(jù)網(wǎng)上一些人的測(cè)試結(jié)果,cglib創(chuàng)建代理對(duì)象的速度要比JDK慢10 ~ 15倍。
適用場(chǎng)景
- 不需要頻繁創(chuàng)建代理對(duì)象的應(yīng)用,如Spring中默認(rèn)的單例bean,只需要在容器啟動(dòng)時(shí)生成一次代理對(duì)象。
以上就是Java 動(dòng)態(tài)代理的多種實(shí)現(xiàn)方式的詳細(xì)內(nèi)容,更多關(guān)于Java 動(dòng)態(tài)代理的實(shí)現(xiàn)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
為什么Spring和IDEA都不推薦使用 @Autowired 注解
本文主要介紹了為什么Spring和IDEA都不推薦使用 @Autowired 注解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04Springboot單體架構(gòu)http請(qǐng)求轉(zhuǎn)換https請(qǐng)求來(lái)支持微信小程序調(diào)用接口
這篇文章主要介紹了Springboot單體架構(gòu)http請(qǐng)求轉(zhuǎn)換https請(qǐng)求來(lái)支持微信小程序調(diào)用接口,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11JAVA實(shí)現(xiàn)生成順序ID,不浪費(fèi)ID
這篇文章主要介紹了JAVA實(shí)現(xiàn)生成順序ID,不浪費(fèi)ID問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-04-04SpringBoot靜態(tài)視頻實(shí)時(shí)播放的實(shí)現(xiàn)代碼
這篇文章主要介紹了SpringBoot靜態(tài)視頻實(shí)時(shí)播放的實(shí)現(xiàn)代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01Struts 2 數(shù)據(jù)校驗(yàn)功能及校驗(yàn)問題的解決方案
這篇文章主要介紹了Struts 2 數(shù)據(jù)校驗(yàn)功能及校驗(yàn)問題的解決方案的相關(guān)資料,需要的朋友可以參考下2016-09-09簡(jiǎn)單了解Spring Cloud Alibaba相關(guān)知識(shí)
這篇文章主要介紹了簡(jiǎn)單了解Spring Cloud Alibaba相關(guān)知識(shí),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10全面了解OAuth?2.0四種授權(quán)方式金三銀四無(wú)懼面試
這篇文章主要介紹了全面了解OAuth?2.0四種授權(quán)方式金三銀四無(wú)懼面試,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02