动态代理,这个词在Java的世界里面常常被提起,尤为是对于部分(这里强调“部分”二字,由于有作了一两年就成大神的,实力强的使人发指,这类人无疑是很是懂动态代理这点小伎俩的)作了一两年新人来讲,老是摸不清楚前因后果,一两年是个坎,为何是一两年,才入门的新人可能对这东西没什么感受,没到这一步,作了好久开发的人显然是明白这其中原理的,而作了一两年的,知其然而不知其因此然,因此一两年工做经验的人不少是很茫然的。java
那么,这里就相对比较比较深刻一点的介绍JDK动态代理的原理。这样子介绍完,明白了其中的道理,我相信你会永远记得JDK动态代理的思想。顺带一句,cglib作的事儿和JDK动态代理作的事儿从结局上来讲差很少,方式不太同样。spring
一、先从JDK的源代码提及,动态代理这部分源码,Oracle版本和OpenJDK的源码是不太同样的,貌似Oracle版本最核心那点东西没开源,F3进去我反正是找不到,我也懒得去找,可是原理都是一致的,这里就挑选OpenJDK的。sql
咱们回顾一下JDK动态代理,先说宏观原理,相信都懂,使用JDK动态代理最多见,至少对于我来讲就是Spring的AOP部分,而且是AOP部分的声明式事务部分。编程
a、定义一个接口Car:mybatis
public interface Car { void drive(String driverName, String carName); }
b、定义接口Car的一个实现类Audi:ide
public class Audi implements Car { @Override public void drive(String driverName, String carName) { System.err.println("Audi is driving... " + "driverName: " + driverName + ", carName" + carName); } }
c、定义一个动态调用的控制器CarHandler:性能
public class CarHandler implements InvocationHandler { private Car car; public CarHandler(Car car) { this.car = car; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.err.println("before"); method.invoke(car, args); System.err.println("after"); return null; } }
d、测试类ProxyTest:测试
public class ProxyTest { @Test public void proxyTest() throws Exception { Car audi = (Car) Proxy.newProxyInstance(Car.class.getClassLoader(), new Class<?>[] {Car.class}, new CarHandler(new Audi())); audi.drive("name1", "audi"); } }
e、输出结果:this
before
Audi is driving... driverName: name1, carNameaudi
after
上面这段,相信你们都懂,也明白原理,这就是所谓的知其然,可是不必定都能知其因此然。接下来就解释下“知其因此然”。spa
进入到Proxy类的newProxyInstance方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { if (h == null) { throw new NullPointerException(); } /* * Look up or generate the designated proxy class. */ Class<?> cl = getProxyClass(loader, interfaces); /* * Invoke its constructor with the designated invocation handler. */ try { Constructor cons = cl.getConstructor(constructorParams); return cons.newInstance(new Object[] { h }); } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); } catch (IllegalAccessException e) { throw new InternalError(e.toString()); } catch (InstantiationException e) { throw new InternalError(e.toString()); } catch (InvocationTargetException e) { throw new InternalError(e.toString()); } }
关键的3行:
// 建立代理类 Class<?> cl = getProxyClass(loader, interfaces); // 实例化代理对象 Constructor cons = cl.getConstructor(constructorParams);
返回的是代理类的实例化对象。接下来的调用就很清晰了。
那么,JDK动态代理最核心的关键就是这个方法:
Class<?> cl = getProxyClass(loader, interfaces);
进入该方法,这个方法很长,前面不少都是铺垫,在方法的最后调用了一个方法:
byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces);
这个方法就是产生代理对象的方法。咱们先不看先后,只关注这一个方法,咱们本身来写一个该方法:
public class ProxyTest { @SuppressWarnings("resource") @Test public void proxyTest() throws Exception { byte[] bs = ProxyGenerator.generateProxyClass("AudiImpl", new Class<?>[] {Car.class}); new FileOutputStream(new File("d:/AudiImpl.class")).write(bs); } }
因而,咱们就在D盘里面看到了一个叫作AudiImpl.class的文件,对该文件进行反编译,获得下面这个类:
public final class AudiImpl extends Proxy implements Car { private static final long serialVersionUID = 5351158173626517207L; private static Method m1; private static Method m3; private static Method m0; private static Method m2; public AudiImpl(InvocationHandler paramInvocationHandler) { super(paramInvocationHandler); } public final boolean equals(Object paramObject) { try { return ((Boolean) this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue(); } catch (Error | RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final void drive(String paramString1, String paramString2) { try { this.h.invoke(this, m3, new Object[] { paramString1, paramString2 }); return; } catch (Error | RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final int hashCode() { try { return ((Integer) this.h.invoke(this, m0, null)).intValue(); } catch (Error | RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final String toString() { try { return (String) this.h.invoke(this, m2, null); } catch (Error | RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m3 = Class.forName("com.mook.core.service.Car").getMethod("drive", new Class[] { Class.forName("java.lang.String"), Class.forName("java.lang.String") }); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); } catch (NoSuchMethodException localNoSuchMethodException) { throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); } catch (ClassNotFoundException localClassNotFoundException) { throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); } } }
这个时候,JDK动态代理全部的秘密都暴露在了你的面前,当咱们调用drive方法的时候,其实是把方法名称传给控制器,而后执行控制器逻辑。这就实现了动态代理。Spring AOP有两种方式实现动态代理,若是基于接口编程,默认就是JDK动态代理,不然就是cglib方式,另外spring的配置文件里面也能够设置使用cglib来作动态代理,关于两者的性能问题,网上也是众说纷纭,不过我我的的观点,性能都不是问题,不太须要去纠结这一点性能问题。
事实上,若是你明白这一点,当你去阅读mybatis源码的时候是颇有帮助的,mybatis的接口方式作方法查询就充分利用了这里的JDK动态代理。不然若是不明白原理,看mybatis的源码的接口方式是很费劲的,固然了,这只是mybatis利用动态代理的冰山一角,要彻底看懂mybaits源码还有其余的许多难点,好比mybatis是以xml文件来配置sql语句的。