老生常談設(shè)計(jì)模式之動(dòng)態(tài)代理
一、動(dòng)態(tài)代理概念
動(dòng)態(tài)代理分為JDK動(dòng)態(tài)代理和cglib動(dòng)態(tài)代理兩種方式。
jdk動(dòng)態(tài)代理是由Java內(nèi)部的反射機(jī)制來實(shí)現(xiàn)的,cglib動(dòng)態(tài)代理底層則是借助asm來實(shí)現(xiàn)的。
總的來說,反射機(jī)制在生成類的過程中比較高效,而asm在生成類之后的相關(guān)執(zhí)行過程中比較高效(可以通過將asm生成的類進(jìn)行緩存,這樣解決asm生成類過程低效問題)。
還有一點(diǎn)必須注意:jdk動(dòng)態(tài)代理的應(yīng)用前提,必須是目標(biāo)類基于統(tǒng)一的接口。如果沒有上述前提,jdk動(dòng)態(tài)代理不能應(yīng)用。
由此可以看出,jdk動(dòng)態(tài)代理有一定的局限性,cglib這種第三方類庫(kù)實(shí)現(xiàn)的動(dòng)態(tài)代理應(yīng)用更加廣泛,且在效率上更有優(yōu)勢(shì)。
二、JDK動(dòng)態(tài)代理
以下代碼使用代理模式實(shí)現(xiàn)一個(gè)大小寫字符轉(zhuǎn)換的功能。
定義接口和實(shí)現(xiàn)類:
ISomeService接口:
package com.ietree.basicskill.designpattern.dynamicproxy.jdk; /** * 接口類 * * @author Root */ public interface ISomeService { String doFirst(); void doSecond(); }
SomeServiceImpl實(shí)現(xiàn)類:
package com.ietree.basicskill.designpattern.dynamicproxy.jdk; /** * 實(shí)現(xiàn)類 * * @author Root */ public class SomeServiceImpl implements ISomeService { @Override public String doFirst() { System.out.println("執(zhí)行doFirst()..."); String result = "abcde"; return result; } @Override public void doSecond() { System.out.println("執(zhí)行doSecond()..."); } }
JDK動(dòng)態(tài)代理類:
package com.ietree.basicskill.designpattern.dynamicproxy.jdk; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class Main { public static void main(String[] args) { final ISomeService target = new SomeServiceImpl(); // 使用JDK的Proxy動(dòng)態(tài)代理,要求目標(biāo)類和代理類必須實(shí)現(xiàn)相同的接口,因?yàn)槠涞讓拥膱?zhí)行原理與靜態(tài)代理的相同 ISomeService service = (ISomeService) Proxy.newProxyInstance( // 目標(biāo)類的類加載器 target.getClass().getClassLoader(), // 目標(biāo)類所實(shí)現(xiàn)的所有接口 target.getClass().getInterfaces(), new InvocationHandler() { // proxy:代理對(duì)象 // method:目標(biāo)方法 // args:目標(biāo)方法的參數(shù)列表 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 調(diào)用目標(biāo)方法 Object result = method.invoke(target, args); if (result != null) { result = ((String) result).toUpperCase(); } return result; } }); String result = service.doFirst(); System.out.println(result); service.doSecond(); } }
三、cglib動(dòng)態(tài)代理
Cglib是一個(gè)優(yōu)秀的動(dòng)態(tài)代理框架,它的底層使用ASM在內(nèi)存中動(dòng)態(tài)的生成被代理類的子類,使用CGLIB即使代理類沒有實(shí)現(xiàn)任何接口也可以實(shí)現(xiàn)動(dòng)態(tài)代理功能。CGLIB具有簡(jiǎn)單易用,它的運(yùn)行速度要遠(yuǎn)遠(yuǎn)快于JDK的Proxy動(dòng)態(tài)代理:
CGLIB的核心類:
net.sf.cglib.proxy.Enhancer – 主要的增強(qiáng)類
net.sf.cglib.proxy.MethodInterceptor – 主要的方法攔截類,它是Callback接口的子接口,需要用戶實(shí)現(xiàn)
net.sf.cglib.proxy.MethodProxy – JDK的java.lang.reflect.Method類的代理類,可以方便的實(shí)現(xiàn)對(duì)源對(duì)象方法的調(diào)用,如使用:
Object o = methodProxy.invokeSuper(proxy, args);//雖然第一個(gè)參數(shù)是被代理對(duì)象,也不會(huì)出現(xiàn)死循環(huán)的問題。
net.sf.cglib.proxy.MethodInterceptor接口是最通用的回調(diào)(callback)類型,它經(jīng)常被基于代理的AOP用來實(shí)現(xiàn)攔截(intercept)方法的調(diào)用。這個(gè)接口只定義了一個(gè)方法
public Object intercept(Object object, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable;
第一個(gè)參數(shù)是代理對(duì)像,第二和第三個(gè)參數(shù)分別是攔截的方法和方法的參數(shù)。原來的方法可能通過使用java.lang.reflect.Method對(duì)象的一般反射調(diào)用,或者使用 net.sf.cglib.proxy.MethodProxy對(duì)象調(diào)用。net.sf.cglib.proxy.MethodProxy通常被首選使用,因?yàn)樗臁?/p>
以下程序?qū)崿F(xiàn)了大小寫轉(zhuǎn)換的功能:
實(shí)現(xiàn)類SomeService:
package com.ietree.basicskill.designpattern.dynamicproxy.cglib; /** * 實(shí)現(xiàn)類 * * @author Root */ public class SomeService { public String doFirst() { System.out.println("執(zhí)行doFirst()..."); String result = "abcde"; return result; } public void doSecond() { System.out.println("執(zhí)行doSecond()..."); } }
代理類MyCglibFactory:
package com.ietree.basicskill.designpattern.dynamicproxy.cglib; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class MyCglibFactory implements MethodInterceptor { private SomeService target; public MyCglibFactory() { super(); target = new SomeService(); } public SomeService myCglibCreator() { // 創(chuàng)建增強(qiáng)器對(duì)象 Enhancer enhancer = new Enhancer(); // 指定目標(biāo)類,即父類 enhancer.setSuperclass(SomeService.class); // 設(shè)置回調(diào)接口對(duì)象 enhancer.setCallback(this); return (SomeService) enhancer.create(); } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { // 調(diào)用目標(biāo)方法 Object result = method.invoke(target, args); if (result != null) { result = ((String) result).toUpperCase(); } return result; } }
測(cè)試:
package com.ietree.basicskill.designpattern.dynamicproxy.cglib; public class Main { public static void main(String[] args) { SomeService service = new MyCglibFactory().myCglibCreator(); String result = service.doFirst(); System.out.println("result = " + result); service.doSecond(); } }
運(yùn)行結(jié)果:
執(zhí)行doFirst()... result = ABCDE 執(zhí)行doSecond()...
以上這篇老生常談設(shè)計(jì)模式之動(dòng)態(tài)代理就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
java實(shí)現(xiàn)表單必填參數(shù)驗(yàn)證的方法
表單校驗(yàn)是很多注冊(cè)時(shí)必做的功能, 一般我們的處理都是很粗暴的寫個(gè)if()判斷, 然后拋異常. 本文將介紹通過代理的思想, 用注解優(yōu)雅的處理非空判斷,感興趣的一起來了解一下2021-05-05詳解獲取Spring MVC中所有RequestMapping以及對(duì)應(yīng)方法和參數(shù)
本篇文章主要介紹了詳解獲取Spring MVC中所有RequestMapping以及對(duì)應(yīng)方法和參數(shù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-03-03java的Map集合中按value值進(jìn)行排序輸出的實(shí)例代碼
下面小編就為大家?guī)硪黄猨ava的Map集合中按value值進(jìn)行排序輸出的實(shí)例代碼。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-08-08