日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

动态代理原理源码分析

發(fā)布時間:2025/3/15 编程问答 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 动态代理原理源码分析 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

看了這篇文章非常不錯轉(zhuǎn)載:https://www.jianshu.com/p/4e14dd223897

Java設(shè)計模式(14)----------動態(tài)代理原理源碼分析

上篇文章《Java設(shè)計模式(13)----------代理模式》中,介紹了兩種代理模式(靜態(tài)代理和動態(tài)代理)的應(yīng)用場景和實際應(yīng)用案例。本篇文章中,對動態(tài)代理的原理做進行深入的分析。

關(guān)于動態(tài)代理,初看可能會比較費解,大概有如下幾個疑問:

  • 代理是怎么形成的,代理類在哪里?
  • TimeHandler類是做什么用的,在哪里被調(diào)用了?
  • 客戶端調(diào)用的時候,寫的是調(diào)用m.move();,程序是如何執(zhí)行到了TimeHandler對象的invoke方法中了呢?

這篇文章,主要針對如上幾個疑問進行展開。

首先聲明一點,案例中的TimeHandler并不是代理類,而是代理依賴的一個類而已。真正的代理類,是JDK直接生成的字節(jié)碼并進行加載的,所以對用戶是不可見的。

生成代理類的代碼是這個:
Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(), h);
這里向newProxyInstance傳遞了三個參數(shù),分別是原始類的加載器,原始類實現(xiàn)的接口(Moveable),TimeHandler類的對象。

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) {Objects.requireNonNull(h);final Class<?> caller = System.getSecurityManager() == null? null: Reflection.getCallerClass();/** Look up or generate the designated proxy class and its constructor.*/Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);return newProxyInstance(caller, cons, h);

newProxyInstance的代碼實現(xiàn)比較清晰,通過getProxyConstructor方法創(chuàng)建了代理類,并返回了該類的構(gòu)造方法。之后使用反射,生成代理類的對象并返回。

private static Constructor<?> getProxyConstructor(Class<?> caller,ClassLoader loader,Class<?>... interfaces){// optimization for single interfaceif (interfaces.length == 1) {Class<?> intf = interfaces[0];if (caller != null) {checkProxyAccess(caller, loader, intf);}return proxyCache.sub(intf).computeIfAbsent(loader,(ld, clv) -> new ProxyBuilder(ld, clv.key()).build());} else {// interfaces clonedfinal Class<?>[] intfsArray = interfaces.clone();if (caller != null) {checkProxyAccess(caller, loader, intfsArray);}final List<Class<?>> intfs = Arrays.asList(intfsArray);return proxyCache.sub(intfs).computeIfAbsent(loader,(ld, clv) -> new ProxyBuilder(ld, clv.key()).build());}}

在getProxyConstructor函數(shù)中,可以看到if-else分支,這里是對單接口的情況做了代碼優(yōu)化。我們主要關(guān)注其代理類的生成部分,就是new ProxyBuilder(ld, clv.key()).build()。

(ld, clv) -> new ProxyBuilder(ld, clv.key()).build() 這種寫法是lambda表達(dá)式的語法,非常精簡。

Constructor<?> build() {Class<?> proxyClass = defineProxyClass(module, interfaces);final Constructor<?> cons;try {cons = proxyClass.getConstructor(constructorParams);} catch (NoSuchMethodException e) {throw new InternalError(e.toString(), e);}AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void run() {cons.setAccessible(true);return null;}});return cons;}

在build方法中,從語意上可以看到代理類proxyClass在defineProxyClass方法中生成。之后通過反射并根據(jù)構(gòu)造函數(shù)的參數(shù),獲取到代理類的構(gòu)造方法并返回。

這里的參數(shù)為private static final Class<?>[] constructorParams = { InvocationHandler.class };。所以看到這里,應(yīng)該明白了TimeHandler的作用了,就是作為代理類的構(gòu)造方法的一個參數(shù),也就是代理類依賴的對象。到這里,也就找到了文章開始處提出的問題二的答案。這里可以猜測一下,代理類中的對應(yīng)接口的方法,應(yīng)該是調(diào)用的TimeHandler類的invoke方法,通過控制invoke的參數(shù),來調(diào)用不同的方法。

在defineProxyClass方法中,最關(guān)鍵的代碼如下:

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces.toArray(EMPTY_CLASS_ARRAY), accessFlags);try {Class<?> pc = UNSAFE.defineClass(proxyName, proxyClassFile,0, proxyClassFile.length,loader, null);reverseProxyCache.sub(pc).putIfAbsent(loader, Boolean.TRUE);return pc;} catch (ClassFormatError e) {/** A ClassFormatError here means that (barring bugs in the* proxy class generation code) there was some other* invalid aspect of the arguments supplied to the proxy* class creation (such as virtual machine limitations* exceeded).*/throw new IllegalArgumentException(e.toString());}

這里先生成字節(jié)碼,之后將其加載到內(nèi)存中。

static byte[] generateProxyClass(final String name,Class<?>[] interfaces,int accessFlags){ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);final byte[] classFile = gen.generateClassFile();if (saveGeneratedFiles) {java.security.AccessController.doPrivileged(new java.security.PrivilegedAction<Void>() {public Void run() {try {int i = name.lastIndexOf('.');Path path;if (i > 0) {Path dir = Paths.get(name.substring(0, i).replace('.', File.separatorChar));Files.createDirectories(dir);path = dir.resolve(name.substring(i+1, name.length()) + ".class");} else {path = Paths.get(name + ".class");}Files.write(path, classFile);return null;} catch (IOException e) {throw new InternalError("I/O exception saving generated file: " + e);}}});}return classFile;}

在generateProxyClass方法中,先生成了ProxyGenerator對象,然后調(diào)用對象的generateClassFile生成字節(jié)碼。可以看到其中有個saveGeneratedFiles的標(biāo)志位,表示是否需要保存生成的字節(jié)碼文件。在generateClassFile方法中,創(chuàng)建了ProxyMethod,字段表和方法表集合,并將內(nèi)容按照字節(jié)碼的規(guī)范寫入到流中。至此代理類就生成了,文章開始處的問題一就回答完畢了

此處代碼中可以根據(jù)語意看到會使用InvocationHandler的方法,目前對java的class文件的格式還不夠了解,有機會研究一下。

剛剛說到代碼中是存在打印生成的代理類的邏輯的,就是saveGeneratedFiles標(biāo)志位,在該變量定義處可以看到:

private static final boolean saveGeneratedFiles =java.security.AccessController.doPrivileged(new GetBooleanAction("jdk.proxy.ProxyGenerator.saveGeneratedFiles")).booleanValue();

表示該變量是由jdk.proxy.ProxyGenerator.saveGeneratedFiles控制的。所以我們在代碼中只需要指定這個值為true就可以了,代碼如下:

import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy;public class Test {public static void main(String[] args) throws Exception{System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");Car car = new Car();Class<?> cls = car.getClass();InvocationHandler h = new TimeHandler(car);Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(), h);m.move();System.out.println("");m.move_back();System.out.println("");System.out.println("");} }

運行之后,就可以在工程目錄下找到對應(yīng)的代理類的class文件啦

使用idea打開,可以看到對應(yīng)的java代碼如下:

// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) //package com.sun.proxy;import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException;public final class $Proxy0 extends Proxy implements Moveable {private static Method m1;private static Method m3;private static Method m2;private static Method m4;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 Exception {try {super.h.invoke(this, m3, (Object[])null);} catch (Exception | 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 void move_back() throws Exception {try {super.h.invoke(this, m4, (Object[])null);} catch (Exception | 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("Moveable").getMethod("move");m2 = Class.forName("java.lang.Object").getMethod("toString");m4 = Class.forName("Moveable").getMethod("move_back");m0 = Class.forName("java.lang.Object").getMethod("hashCode");} catch (NoSuchMethodException var2) {throw new NoSuchMethodError(var2.getMessage());} catch (ClassNotFoundException var3) {throw new NoClassDefFoundError(var3.getMessage());}} }

代理類解讀:

  • 代理類的構(gòu)造方法,接收一個InvocationHandler對象,調(diào)用父類Proxy的初始化方法,使用該對象為依賴h的對象賦值。
  • 以move方法為例,我們可以看到,代理類實現(xiàn)了Moveable接口,在move方法的實現(xiàn)中,調(diào)用了InvocationHandler對象的invoke方法,并且其中第二個參數(shù)為m3對象。而m3對象是靜態(tài)塊中通過反射獲取到的Moveable接口的move方法。所以,此處就回答的文章開始處的問題三。

到這里,動態(tài)代理的原理就非常清晰了,代理類是jdk直接以字節(jié)碼的形式生成出來的,繼承Proxy類,實現(xiàn)客戶端定義的接口。因為Proxy依賴InvocationHandler實現(xiàn)類,所以代理類也同樣依賴。對于接口中定義的函數(shù)move,代理類中也會實現(xiàn),其邏輯是調(diào)用InvocationHandler類中的invoke方法,并將方法move方法作為invoke的一個參數(shù)。在invoke方法中,可以進行功能擴展,進而執(zhí)行invoke參數(shù)中的方法,完成對原始對象的move方法的調(diào)用。不得不佩服java源碼的作者們的深厚功力,能夠設(shè)計出如此高擴展的結(jié)構(gòu),自己需要學(xué)習(xí)和實踐的還有很多。

總結(jié)

以上是生活随笔為你收集整理的动态代理原理源码分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。