動態代理,這個詞在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語句的。