JDK动态代理原理解析
JDK动态代理原理解析
本文章不介绍什么是动态代理,而是讲解原理
Demo代码
我有一个Animal接口
public interface Animal { void eat(); }
有一个Dog类实现了Animal接口
public class Dog implements Animal { @Override public void eat() { System.out.println("animal eat..."); } }
现在我想调用eat方法之前打印日志"before",之后打印"after", 我的代码如下
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /**
* @author hh
* @date 2020/9/29
*/ public class JdkProxyDemo implements InvocationHandler { static Animal animal = new Dog(); public static void main(String[] args) { Animal proxyAnimal = (Animal) Proxy.newProxyInstance( JdkProxyDemo.class.getClassLoader(), new Class[]{Animal.class}, new JdkProxyDemo()); System.out.println(proxyAnimal.getClass().getName()); proxyAnimal.eat(); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before..."); Object invoke = method.invoke(animal, args); System.out.println("after ..."); return invoke; } }
执行上面main方法,成功的达到了我想要的结果
com.sun.proxy.$Proxy0
before... animal eat... after ...
解密Animal animal = (Animal) Proxy.newProxyInstance(xxx)
这个animal实例就是一个jdk使用sun.misc.ProxyGenerator生成的代理对象(sun.misc.ProxyGenerator#generateProxyClass),ProxyGenerator的原理就是一个懂class文件规范的人,在代码里直接生产class文件字节码,然后使用ClassLoader加载到内存,有兴趣可以自己研究。
代理类animal的class文件如下:
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 Animal { private static Method m1; private static Method m3; private static Method m2; private static Method m0; public $Proxy0(InvocationHandler var1) { super(var1); } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m3 = Class.forName("hh.proxy.jdk.Animal").getMethod("eat"); 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()); } } public final boolean equals(Object var1) { 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() { 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() { try { return (Integer)super.h.invoke(this, m0, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final void eat() { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } }
我们调用eat方法,实际上调用了代理类的eat方法:
public final void eat() { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } }
h就是InvocationHandler,也就是我上面的main方法所在的类, 他实现了这个接口
this就是proxy类,所以在:
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before..."); Object invoke = method.invoke(animal, args); System.out.println("after ..."); return invoke; }
方法中不能使用proxy, 他是代理类, 如果使用了他就是代理类自己玩自己, 一直玩下去,递归调用
至于为什么super.h.invoke(this, m3, (Object[])null); 方法是m3=eat方法, 因为这个m3也是动态代理作者起的名字,他当然知道m3是哪个方法了.
可以debug吗
不可以, 因为就算你把$proxy0反编译放到idea中, 他的字节码行号和源码行号对应不上,debug就算生效,行号也是对不上的, 但是,你可以debug的时候切换到proxy类, 看他的static字段,也就是m1,m2.m3…, 你可以看到m3确实是eat方法,比如:
怎么生成proxy源代码
使用ProxyGenerator, 或者使用hsdb工具, 这个可以百度搜索一大堆, 这里就不介绍了
与cglib有什么区别
- 1、jdk代理需要有一个接口
- 2、jdk代理最后走的还是method.invoke(…)使用的是反射,效率比较低(可以是试试class.newInstace()和new Object()的效率就知道了)
本文地址:https://blog.csdn.net/qq_31543867/article/details/108875947