JDK动态代理源码分析
准备如下:
public interface Subject { void request(String req); } //需要被代理的类 public class RealSubject implements Subject { @Override public void request(String req) { System.out.println(req); } } //代理方法执行的类 public class MyInvocationHander implements InvocationHandler { private Subject subject; public MyInvocationHander(Subject subject) { this.subject = subject; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before ...."); method.invoke(subject,args); //被代理的方法 System.out.println("after ...."); return null; } } //测试类 public class Demo { public static void main(String[] args) { Subject sub=new RealSubject(); MyInvocationHander h = new MyInvocationHander(sub); Subject proxySub =(Subject) Proxy.newProxyInstance(sub.getClass().getClassLoader(), sub.getClass().getInterfaces(), h); proxySub.request("haha"); } }
//使用idea打断点
//看源码切记别扣细节,Proxy.newProxyInstance知道是通过字节码对象反射创建的代理对象,所以目标很明确,需要找到对应的字节码对象
进入该方法:getProxyClass0
进入该方法(proxyClassCache.get(loader, interfaces): 这里是重点,如果扣细节,很难找到,使用目标定位法(返回值),直接找到return值的方法
点get方法后,有很多实现类,该supplier的实现类和方法如下:
在get方法找到返回V的地方(目标搜索法,也叫返回值法,这个是看源码的技巧):
进入valueFactory.apply(key, parameter)方法:
字节码是二进制文件,所以ProxyGenerator.generateProxyClass该方法是生成字节码二进制数组;
点击进入了:
这个类根据字节码的结构进行生成对应的字节码文件的
这里的saveGeneratedFiles是一个boolen类型的系统常量,见明之意就是保存生成的字节码文件,默认为false;
因此,我们设置System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles","true");这个属性就可以生成动态代理的字节码文件了;
进入:
可见,Object对象的hashcode,equals 和 toString方法都会被代理;
//设置系统属性,生成字节码看看:
执行后,可以看到:
idea打开,会进行反编译,如下:
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package com.sun.proxy; import com.yang.jvm.Subject; 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 Subject { private static Method m1; private static Method m2; 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})).booleanValue(); } 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 request(String var1) throws { try { 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)).intValue(); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")}); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m3 = Class.forName("com.yang.jvm.Subject").getMethod("request", new Class[]{Class.forName("java.lang.String")}); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
//根据字节码可以知道,我们调用代理类的方法时,本质调用的是InvocationHandler的invoke方法,而InvocationHandler是我们自己实现的
最终在被代理对象的方法执行前后进行了相应的业务代码;
问题1: jdk动态代理为何只能对接口起作用,不能是类?
原因:
public final class $Proxy0 extends Proxy implements Subject {}
动态代理生成的类会继承Proxy,而java是单继承的,所以不能再继承了,所以只能通过实现来拓展
问题2: InvocationHandler是什么时候传入动态代理类的呢?
Proxy的源码,有个构造器:
源码生成字节码后,通过反射创建对象时,传入了h:
以上就是jdk动态代理原理了! spring为何使用了jdk动态代理还要使用cglib动态代理呢?原因跟第一个问题答案一样