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