Java中的動(dòng)態(tài)代理使用
動(dòng)態(tài)代理
動(dòng)態(tài)代理的時(shí)候,定義一個(gè)接口,需要代理人和被代理類實(shí)現(xiàn)這個(gè)接口,這樣不夠靈活,代理類能夠代理的類只有實(shí)現(xiàn)這個(gè)接口的類。
非常不靈活,假如被代理人的類沒(méi)有實(shí)現(xiàn)這個(gè)接口,那么就需重新寫一個(gè)代理類。對(duì)于日志、事務(wù)這些操作是不區(qū)分業(yè)務(wù)的,即不需要規(guī)定都實(shí)現(xiàn)某接口。因此,出現(xiàn)了動(dòng)態(tài)代理
java種的動(dòng)態(tài)代理生成的方式大概有三種JDK動(dòng)態(tài)代理、instrument動(dòng)態(tài)代理、cglib動(dòng)態(tài)代理。其中,前兩種是JDK自帶的,cglib是需要第三方依賴使用的。
- JDK動(dòng)態(tài)代理:在程序運(yùn)行的過(guò)程種動(dòng)態(tài)的生成代理類,這個(gè)代理類實(shí)現(xiàn)被代理類的接口,因此使用時(shí)候被代理類必須實(shí)現(xiàn)接口;
- instrument動(dòng)態(tài)代理:是通過(guò)Class字節(jié)文件在加載至內(nèi)存的過(guò)程種添加攔截器,動(dòng)態(tài)的修改字節(jié)文件實(shí)現(xiàn)的;
- cglib:在程序運(yùn)行的過(guò)程種動(dòng)態(tài)生成代理類,這個(gè)代理類是通過(guò)繼承被代理類實(shí)現(xiàn)的,因此不能代理被final修飾的類
- JDK動(dòng)態(tài)代理和cglib動(dòng)態(tài)代理的底層都是ASM;
JDK動(dòng)態(tài)代理
先看這樣一個(gè)程序
/** 接口*/ public interface Mobile { void move(); } /** 被代理類*/ public class Car implements Mobile{ @Override public void move() { System.out.println("move..."); } } /** 使用動(dòng)態(tài)代理*/ public class Test { public static void main(String[] args) { Car car = new Car(); System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles",true); Mobile proxyInstance = (Mobile) Proxy.newProxyInstance(Car.class.getClassLoader(), Car.class.getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long before = System.currentTimeMillis(); System.out.println(method.getName()+"方法被執(zhí)行前。。。"); Object invoke = method.invoke(car, args); System.out.println(method.getName()+"方法被執(zhí)行后。。。"); long after = System.currentTimeMillis(); System.out.println("耗時(shí):" + (after - before)); return null; } }); proxyInstance.move(); } }
創(chuàng)建代理類需要調(diào)用Proxy.newProxyInstance()方法,其中有三個(gè)參數(shù)
- 第一個(gè)參數(shù):一個(gè)類加載器,一般使用被代理類的類加載器
- 第二個(gè)參數(shù):被代理類實(shí)現(xiàn)的接口
- 第三個(gè)參數(shù):一個(gè)InvocationHandler接口的實(shí)現(xiàn)類
可以把動(dòng)態(tài)代理的創(chuàng)建改成這樣:自己寫一個(gè)類實(shí)現(xiàn)InvocationHandler。
public class Test { public static void main(String[] args) { Car car = new Car(); System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true"); Mobile proxyInstance = (Mobile) Proxy.newProxyInstance(Car.class.getClassLoader(), Car.class.getInterfaces(), new MyInvocationHandler(car)); proxyInstance.move(); } } class MyInvocationHandler implements InvocationHandler{ private Car car; public MyInvocationHandler(Car car) { this.car = car; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long before = System.currentTimeMillis(); System.out.println(method.getName()+"方法被執(zhí)行前。。。"); Object invoke = method.invoke(car, args); System.out.println(method.getName()+"方法被執(zhí)行后。。。"); long after = System.currentTimeMillis(); System.out.println("耗時(shí):" + (after - before)); return null; } }
要想搞懂JDK動(dòng)態(tài)代理必須查看動(dòng)態(tài)生成代理類,通過(guò)設(shè)置JDK動(dòng)態(tài)代理生成的類是否保存的一個(gè)屬性將生成的代理類保存下來(lái),通過(guò)在程序啟動(dòng)前加上:
System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles",true);
代理類的調(diào)用過(guò)程
- 生成的代理類
public final class $Proxy0 extends Proxy implements Mobile { private static Method m1; private static Method m3; private static Method m2; private static Method m0; public $Proxy0(InvocationHandler var1) throws { super(var1); } public final boolean equals(Object var1) throws { try { return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final void move() throws { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final String toString() throws { try { return (String)super.h.invoke(this, m2, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final int hashCode() throws { try { return (Integer)super.h.invoke(this, m0, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m3 = Class.forName("com.shaoby.basic.proxy.JdkProxy.Mobile").getMethod("move"); m2 = Class.forName("java.lang.Object").getMethod("toString"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
- 可以看到代理類繼承了Proxy,并實(shí)現(xiàn)了我們定義的Mobile接口
- 構(gòu)造方法傳入InvocationHandler類型,并賦值給父類Proxy中的h參數(shù),可以猜測(cè)這個(gè)就在獲取代理類時(shí)候的第三個(gè)參數(shù);
- 代理類中通過(guò)反射生成了四個(gè)方法,除了Object中的equals、toString、hashCode外另外一個(gè)就是我們要代理的方法move;
- 在move方法中調(diào)用了 super.h.invoke(this, m3, (Object[])null),即MyInvocationHandler中的invoke方法。
- 因此在調(diào)用代理類的invoke方法時(shí),調(diào)用的就是MyInvocationHandler中的invoke方法。
Method類
這個(gè)類是一個(gè)方法的類,說(shuō)白了就是方法的模板,在這個(gè)類中有一個(gè)方法invoke,傳入兩個(gè)參數(shù),一個(gè)是調(diào)用方法的對(duì)象,另一個(gè)是方法的入?yún)?/p>
InvocationHandler中的invoke方法
可以看到在代理類中invoke方法的調(diào)用為 super.h.invoke(this, m3, (Object[])null);
MyInvocationHandler中的invoke方法:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long before = System.currentTimeMillis(); System.out.println(method.getName()+"方法被執(zhí)行前。。。"); Object invoke = method.invoke(car, args); System.out.println(method.getName()+"方法被執(zhí)行后。。。"); long after = System.currentTimeMillis(); System.out.println("耗時(shí):" + (after - before)); return null; }
參數(shù):
- 第一個(gè)參數(shù):this,即代理類本身
- 第二個(gè)參數(shù):m3,即move方法
- 第三個(gè)參數(shù):第二個(gè)方法的入?yún)?/li>
返回值:
這個(gè)返回值其實(shí)是被代理方法的返回值,如果沒(méi)有返回值就返回null;
這里最重要的是method.invoke(car, args),就是用car對(duì)象調(diào)用move方法。
ASM
在上述分析中,動(dòng)態(tài)代理類的構(gòu)造方法中參數(shù)是我們猜測(cè)的。其實(shí)這里是用ASM實(shí)現(xiàn)的。在生成代理類時(shí)調(diào)用的Proxy.newInstance()傳入了InvocationHandler接口的實(shí)現(xiàn)類,Proxy.neInstance()會(huì)動(dòng)態(tài)的生成代理類,并產(chǎn)生一個(gè)代理對(duì)象。這里后續(xù)再研究。
ASM是Java字節(jié)碼操控框架,它能夠以二進(jìn)制形式修改已有類,或動(dòng)態(tài)生成類,ASM 可以直接產(chǎn)生二進(jìn)制 class 文件,也可以在類被加載入 Java 虛擬機(jī)之前動(dòng)態(tài)改變類行為。ASM 從類文件中讀入信息后,能夠改變類行為,分析類信息,甚至能夠根據(jù)用戶要求生成新類。
cglib動(dòng)態(tài)代理
public class Test { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Plane.class); enhancer.setCallback(new MyMethodInterceptor()); Plane o = (Plane)enhancer.create(); o.move(); } } class MyMethodInterceptor implements MethodInterceptor{ @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { long before = System.currentTimeMillis(); System.out.println(method.getName()+"方法被執(zhí)行前。。。"); Object result = methodProxy.invokeSuper(o, objects); System.out.println(method.getName()+"方法被執(zhí)行后。。。"); long after = System.currentTimeMillis(); System.out.println("耗時(shí):" + (after - before)); return result; } }
cglib動(dòng)態(tài)代理的原理是:
- 指定代理類的父類
- 生成代理類
- 調(diào)用代理類父類的method
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
MyBatisPlus3如何向數(shù)據(jù)庫(kù)中存入List
本文主要介紹了Mybatis Plus的類型處理器的使用,通過(guò)User.java和UserMapper.xml示例進(jìn)行詳細(xì)的解析,并提供了JSON解析器的使用方法,希望通過(guò)這篇文章,可以幫助大家更好的理解和掌握Mybatis Plus的類型處理器2024-10-10詳解在springmvc中解決FastJson循環(huán)引用的問(wèn)題
本篇文章主要介紹了在springmvc中解決FastJson循環(huán)引用的問(wèn)題,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-01-01IntelliJIDEA中實(shí)現(xiàn)SpringBoot多實(shí)例運(yùn)行的兩種方式
在微服務(wù)開發(fā)中,經(jīng)常需要同時(shí)啟動(dòng)多個(gè)服務(wù)實(shí)例進(jìn)行測(cè)試或模擬集群環(huán)境,?IntelliJ?IDEA?作為Java開發(fā)者常用工具,提供了靈活的多實(shí)例啟動(dòng)支持,本文將詳細(xì)介紹如何通過(guò)修改配置?和批量啟動(dòng)?兩種方式實(shí)現(xiàn)SpringBoot多實(shí)例運(yùn)行,并解決常見問(wèn)題,需要的朋友可以參考下2025-03-03java 動(dòng)態(tài)生成SQL的實(shí)例講解
下面小編就為大家?guī)?lái)一篇java 動(dòng)態(tài)生成SQL的實(shí)例講解。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-07-07spring mvc4中相關(guān)注解的詳細(xì)講解教程
這篇文章主要給大家介紹了關(guān)于spring mvc4中相關(guān)注解的相關(guān)資料,其中詳細(xì)介紹了關(guān)于@Controller、@RequestMapping、@RathVariable、@RequestParam及@RequestBody等等注解的相關(guān)內(nèi)容,需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-06-06使用IntelliJ IDEA查看類的繼承關(guān)系圖形(圖文詳解)
這篇文章主要介紹了使用IntelliJ IDEA查看類的繼承關(guān)系圖形,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的工作或?qū)W習(xí)具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-03-03