JDK动态代理深入理解分析并手写简易JDK动态代理(上)
Posted petewell
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDK动态代理深入理解分析并手写简易JDK动态代理(上)相关的知识,希望对你有一定的参考价值。
原文引用https://www.dazhuanlan.com/2019/08/26/5d6300df6f20f/
博客真的是好几个月没更了,2019新年第一篇,继续深入动态代理,前两篇简单分析了动态代理的实现原理之后,这次继续深入了解具体的实现方式,并手写一套简易的动态代理已加强理解;
本博客关于Java动态代理相关内容直达链接:
- JDK动态代理浅析
- Cglib动态代理浅析
- JDK动态代理深入理解分析并手写简易JDK动态代理(上)
- JDK动态代理深入理解分析并手写简易JDK动态代理(下)
博客真的是好几个月没更了,2019新年第一篇,继续深入动态代理,前两篇简单分析了动态代理的实现原理之后,这次继续深入了解具体的实现方式,并手写一套简易的动态代理已加强理解;
先接上一篇代码,这里先上代码说话,一个简单案列,代理找对象和找工作:
JDK动态代理只能代理有接口的类,定义Persion接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
package com.guitu18.study.proxy;
public interface {
String findLove(String condition);
void findWord(); }
|
实现接口的类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
package com.guitu18.study.proxy;
public class ZhangKuan implements Persion { public String findLove(String condition) { System.out.println(condition); return "叶青我爱你"; } @Override public void findWord() { System.out.println("我想找月薪15-25k的工作"); } }
|
代理类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
|
package com.guitu18.study.proxy.jdk; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;
public class JDKProxy { public static Object getProxyInstance(final Object target) { return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("开始事务..."); Object invoke = method.invoke(target, args); System.out.println("提交/回滚事务..."); return invoke; } }); } }
|
代理测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
package com.guitu18.study.proxy.jdk; import com.guitu18.study.proxy.Persion; import com.guitu18.study.proxy.ZhangKuan; import sun.misc.ProxyGenerator; import java.io.FileOutputStream;
public class JDKProxyTest { public static void main(String[] args) { try { Persion persion = (Persion) JDKProxy.getProxyInstance(new ZhangKuan()); String love = persion.findLove("肤白貌美大长腿"); System.out.println(love); System.out.println(persion.getClass());
byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Persion.class}); FileOutputStream fos = new FileOutputStream("D://$Proxy0.class"); fos.write(bytes); System.out.println("文档写入成功"); } catch (Exception e) { e.printStackTrace(); } } }
|
到这里,代理任务已经可以完成看;此处,我们将JDK动态代理生成的代理类,通过流的形式保存到磁盘中了,现在使用idea自带反编译工具查看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
|
import com.guitu18.study.proxy.Persion; 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 Persion { private static Method m1; private static Method m2; private static Method m4; private static Method m3; 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 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 findWord() throws { try { super.h.invoke(this, m4, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } }
public final String findLove(String var1) throws { try { return (String)super.h.invoke(this, m3, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } }
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")); m2 = Class.forName("java.lang.Object").getMethod("toString"); m4 = Class.forName("com.guitu18.study.proxy.Persion").getMethod("findWord"); m3 = Class.forName("com.guitu18.study.proxy.Persion").getMethod("findLove", Class.forName("java.lang.String")); 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类并实现了我们的接口,并生成了被代理类的全部方法,包括toString、equals等等所有方法; 这里生成的代理类继承自Proxy,在Proxy中有这么一段代码:
1 2 3 4 5
|
protected InvocationHandler h;
|
这就是为什么我们在生成代理类的时候,要传入的一个InvocationHandler的实现,我们所有的代理增强代码都写在invoke()方法中,而最终代理类执行代理也是通过调用父类Proxy中的InvocationHandler接口的invoke()方法;
而我们知道Java是单继承的,所以代理类只能去实现我们的接口以供我们可以将其强转;
在我们调用自身方法时,代理类巧妙的通过调用生成的同名方法然后调用super.h.invoke()方法,最终就到了我们实现InvocationHandler时所写的invoke()增强方法;在此时,method.invoke()才真正通过反射调用了我们自身所写的方法;这种极为巧妙无侵入式的方法增强实在是妙,令人叹为观止;
这里推荐一篇 劳夫子 的 [ ProxyGenerator生成代理类的字节码文档解析 ];
分析的是ProxyGenerator.generateClassFile()字节码生成原理,写的非常好,值得好好看一下;
下一篇,通过手写一套简易的JDK动态代理的实现增强理解;
以上是关于JDK动态代理深入理解分析并手写简易JDK动态代理(上)的主要内容,如果未能解决你的问题,请参考以下文章
深入理解设计模式-代理模式(静态代理动态代理jdk和cglib)
深入理解设计模式-代理模式(静态代理动态代理jdk和cglib)
JDK动态代理 cglib动态代理的深入理解
JDK动态代理源码分析
深入理解JDK动态代理
代理模式-3(手写实现JDK动态代理)