动态代理原理源码分析
看了這篇文章非常不錯轉(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)代理的原理做進(jìn)行深入的分析。
關(guān)于動態(tài)代理,初看可能會比較費解,大概有如下幾個疑問:
- 代理是怎么形成的,代理類在哪里?
- TimeHandler類是做什么用的,在哪里被調(diào)用了?
- 客戶端調(diào)用的時候,寫的是調(diào)用m.move();,程序是如何執(zhí)行到了TimeHandler對象的invoke方法中了呢?
這篇文章,主要針對如上幾個疑問進(jìn)行展開。
首先聲明一點,案例中的TimeHandler并不是代理類,而是代理依賴的一個類而已。真正的代理類,是JDK直接生成的字節(jié)碼并進(jìn)行加載的,所以對用戶是不可見的。
生成代理類的代碼是這個:
Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(), h);
這里向newProxyInstance傳遞了三個參數(shù),分別是原始類的加載器,原始類實現(xiàn)的接口(Moveable),TimeHandler類的對象。
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文件的格式還不夠了解,有機(jī)會研究一下。
剛剛說到代碼中是存在打印生成的代理類的邏輯的,就是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代碼如下:
代理類解讀:
- 代理類的構(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方法中,可以進(jìn)行功能擴(kuò)展,進(jìn)而執(zhí)行invoke參數(shù)中的方法,完成對原始對象的move方法的調(diào)用。不得不佩服java源碼的作者們的深厚功力,能夠設(shè)計出如此高擴(kuò)展的結(jié)構(gòu),自己需要學(xué)習(xí)和實踐的還有很多。
總結(jié)
以上是生活随笔為你收集整理的动态代理原理源码分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C语言再学习 -- 位、字节、字、字长、
- 下一篇: 最优化学习笔记(十八)——拟牛顿法(4)