經典三層框架初識(二)---Spring 3.2動態代理
上面的遺留問題需要我們使用這次講的動態代理來解決.由於在網上看到一個關於動態代理富含原始碼解釋的文章,覺得很棒.這裡就通過轉載的方式和大家分享一下.
動態代理(JDK代理):
-
動態代理有以下特點:
- 代理物件,不需要實現介面
-
代理物件的生成,是利用JDK的API,動態的在記憶體中建立代理物件(需要我們指定建立代理物件/目標物件實現的介面的型別)
- 動態代理也叫做:JDK代理,介面代理
-
JDK中生成代理物件的API
- 代理類所在包:java.lang.reflect.Proxy.JDK實現代理只需要使用newProxyInstance方法,但是該方法需要接收三個引數,完整的寫法是:static Object newProxyInstance(ClassLoader loader, Class<?>[ ] interfaces,InvocationHandler h )
- 方法是在Proxy類中是靜態方法,且接收的三個引數依次為:
- ClassLoader loader //指定當前目標物件使用類載入器
- Class<?>[ ] interfaces //目標物件實現的介面的型別,使用泛型方式確認型別
- InvocationHandler h //事件處理器
-
下面進行程式碼演示:
- 介面類IUserDao
-
/** * 介面 */ public interface IUserDao { void save(); }
目標物件UserDao
-
/** * 介面實現 * 目標物件 */ public class UserDao implements IUserDao { public void save() { System.out.println("----儲存資料成功!----"); } }
代理工廠類:ProxyFactory
-
/** * 建立動態代理物件 * 動態代理不需要實現介面,但是需要指定介面型別 */ public class ProxyFactory{ //維護一個目標物件 private Object target; public ProxyFactory(Object target){ this.target=target; } //給目標物件生成代理物件 public Object getProxyInstance(){ 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("開始事務111"); //執行目標物件方法 Object returnValue = method.invoke(target, args); System.out.println("提交事務111"); return returnValue; } } ); } }
測試類:App:
-
/** * 測試類 */ public class App { public static void main(String[] args) { // 目標物件 IUserDao target = new UserDao(); // 【原始的型別 class com.zhong.UserDao】 System.out.println(target.getClass()); // 給目標物件,建立代理物件 IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance(); // class $Proxy0 記憶體中動態生成的代理物件 System.out.println(proxy.getClass()); // 執行方法 【代理物件】 proxy.save(); } }
在這裡我們會想:代理物件是誰,是如何生成這個代理物件的呢?接下來我們主要看這個方法 getProxyInstance()
-
//給目標物件生成代理物件 public Object getProxyInstance(){ 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("開始事務111"); //執行目標物件方法 Object returnValue = method.invoke(target, args); System.out.println("提交事務111"); return returnValue; } } );
我們看到其返回了一個Proxy類的物件,即JDK的動態代理,是通過一個叫Proxy的類的靜態方法newProxyInstance來實現的,其那麼我們就去它的原始碼裡看一下它到底都做了些什麼?
-
public static Object newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h) throws IllegalArgumentException { //檢查h 不為空,否則拋異常 Objects.requireNonNull(h); final Class>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * 獲得與指定類裝載器和一組介面相關的代理類型別物件 */ Class> cl = getProxyClass0(loader, intfs); /* * 通過反射獲取建構函式物件並生成代理類例項 */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } //獲取代理物件的構造方法(也就是$Proxy0(InvocationHandler h)) final Constructor> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction() { public Void run() { cons.setAccessible(true); return null; } }); } //生成代理類的例項並把InvocationHandlerImpl的例項傳給它的構造方法 return cons.newInstance(new Object[]{h}); } catch (IllegalAccessException|InstantiationException e) { throw new InternalError(e.toString(), e); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString(), t); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } }
上面的程式碼表明,首先通過getProxyClass0獲得這個代理類,然後通過c1.getConstructor()拿到建構函式,最後一步,通過cons.newInstance返回這個新的代理類的一個例項,注意:呼叫newInstance的時候,傳入的引數為h,即我們自己定義好的InvocationHandler類 。我們再進去getProxyClass0方法看一下:
-
/** * Generate a proxy class. Must call the checkProxyAccess method * to perform permission checks before calling this. */ private static Class> getProxyClass0(ClassLoader loader, Class>... interfaces) { if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } // If the proxy class defined by the given loader implementing // the given interfaces exists, this will simply return the cached copy; // otherwise, it will create the proxy class via the ProxyClassFactory return proxyClassCache.get(loader, interfaces); }
這裡用到了快取,先從快取裡查一下,如果存在,直接返回,不存在就新建立。
真相還是沒有來到,繼續,看一下proxyClassCache
-
/** * a cache of proxy classes */ private static final WeakCache[], Class>> proxyClassCache = new WeakCache(new KeyFactory(), new ProxyClassFactory());
再看下proxyClassCache.get方法,
-
public synchronized V get() { // serialize access // re-check Supplier supplier = valuesMap.get(subKey); if (supplier != this) { // something changed while we were waiting: // might be that we were replaced by a CacheValue // or were removed because of failure -> // return null to signal WeakCache.get() to retry // the loop return null; } // else still us (supplier == this) // create new value V value = null; try { value = Objects.requireNonNull(valueFactory.apply(key, parameter)); } finally { if (value == null) { // remove us on failure valuesMap.remove(subKey, this); } } // the only path to reach here is with non-null value assert value != null; // wrap value with CacheValue (WeakReference) CacheValue cacheValue = new CacheValue(value); // try replacing us with CacheValue (this should always succeed) if (valuesMap.replace(subKey, this, cacheValue)) { // put also in reverseMap reverseMap.put(cacheValue, Boolean.TRUE); } else { throw new AssertionError("Should not reach here"); } // successfully replaced us with new CacheValue -> return the value // wrapped by it return value; } }
其中,value = Objects.requireNonNull(valueFactory.apply(key, parameter));
提到了apply(),是Proxy類的內部類ProxyClassFactory實現其介面的一個方法,具體實現如下:
-
/** * A factory function that generates, defines and returns the proxy class given * the ClassLoader and array of interfaces. */ private static final class ProxyClassFactory implements BiFunction[], Class>> { // prefix for all proxy class names private static final String proxyClassNamePrefix = "$Proxy"; // next number to use for generation of unique proxy class names private static final AtomicLong nextUniqueNumber = new AtomicLong(); @Override public Class> apply(ClassLoader loader, Class>[] interfaces) { Map, Boolean> interfaceSet = new IdentityHashMap(interfaces.length); for (Class> intf : interfaces) { /* * Verify that the class loader resolves the name of this * interface to the same Class object. */ Class> interfaceClass = null; try { interfaceClass = Class.forName(intf.getName(), false, loader); } catch (ClassNotFoundException e) { } if (interfaceClass != intf) { throw new IllegalArgumentException( intf + " is not visible from class loader"); } /* * Verify that the Class object actually represents an * interface. */ if (!interfaceClass.isInterface()) { throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } /* * Verify that this interface is not a duplicate. */ if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } } String proxyPkg = null; // package to define proxy class in int accessFlags = Modifier.PUBLIC | Modifier.FINAL; /* * Record the package of a non-public proxy interface so that the * proxy class will be defined in the same package. Verify that * all non-public proxy interfaces are in the same package. */ for (Class> intf : interfaces) { int flags = intf.getModifiers(); if (!Modifier.isPublic(flags)) { accessFlags = Modifier.FINAL; String name = intf.getName(); int n = name.lastIndexOf('.'); String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); if (proxyPkg == null) { proxyPkg = pkg; } else if (!pkg.equals(proxyPkg)) { throw new IllegalArgumentException( "non-public interfaces from different packages"); } } } if (proxyPkg == null) { // if no non-public proxy interfaces, use com.sun.proxy package proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; } /* * Choose a name for the proxy class to generate. */ long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg + proxyClassNamePrefix + num; /* * Generate the specified proxy class. */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { /* * A ClassFormatError here means that (barring bugs in the * proxy class generation code) there was some other * invalid aspect of the arguments supplied to the proxy * class creation (such as virtual machine limitations * exceeded). */ throw new IllegalArgumentException(e.toString()); } } }
這裡我們看到了熟悉的方法Class.forName();要載入指定的介面,即是生成類,那就有對應的class位元組碼
-
/生成位元組碼 byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
接下來我們也使用測試一下,使用這個方法生成的位元組碼是個什麼樣子:
-
package com.adam.java.basic; import java.io.FileOutputStream; import java.io.IOException; import sun.misc.ProxyGenerator; public class DynamicProxyTest { public static void main(String[] args) { IUserDao userdao = new UserDao(); ProxyFactory handler = new ProxyFactory ( userdao); IUserDao proxy = (IUserDao ) handler.getProxyInstance(); proxy.save(); String path = "C:/$Proxy0.class"; byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", UserDao.class.getInterfaces()); FileOutputStream out = null; try { out = new FileOutputStream(path); out.write(classFile); out.flush(); } catch (Exception e) { e.printStackTrace(); } finally { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } }
不是原始的IUserDao裡的save()方法了,而是新生成的代理類的save()方法,我們將生成的$Proxy0.class檔案用jd-gui開啟
-
public final void save() throws { try { this.h.invoke(this, m3, null); return; } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } }
核心就在於this.h.invoke(this. m3, null);此處的h是啥呢?我們看看這個類的類名:
public final class $Proxy0 extends Proxy implements IUserDao
不難發現,新生成的這個類,繼承了Proxy類實現了IUserDao這個介面,而這個UserService就是我們指定的介面,所以,這裡我們基本可以斷定,JDK的動態代理,生成的新代理類就是繼承了Proxy基類,實現了傳入的介面的類。那這個h到底是啥呢?我們再看看這個新代理類,看看建構函式:
-
public $Proxy0(InvocationHandler paramInvocationHandler) throws { super(paramInvocationHandler); }
這裡傳入了InvocationHandler型別的引數,而之前有一句程式碼:
return cons.newInstance(new Object[]{h});
這是newInstance方法的最後一句,傳入的h,就是這裡用到的h,也就是我們最初自己定義的MyInvocationHandler類的例項。所以,我們發現,其實最後呼叫的save()方法,其實呼叫的是ProxyFactory的invoke()方法.繼續看:
-
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.zhong.IUserDao").getMethod("save", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); return; }
m3就是原介面的save()方法.
-
通過跟蹤提示程式碼可以看出:當代理物件呼叫真實物件的方法時,其會自動的跳轉到代理物件關聯的handler物件的invoke方法來進行呼叫。
-
總結:
- 動態代理實現過程:
- 1. 通過getProxyClass0()生成代理類。JDK生成的最終真正的代理類,它繼承自Proxy並實現了我們定義的介面.
- 2. 通過Proxy.newProxyInstance()生成代理類的例項物件,建立物件時傳入InvocationHandler型別的例項。
- 3. 呼叫新例項的方法,即此例中的save(),即原InvocationHandler類中的invoke()方法。
- 代理物件不需要實現介面,但是目標物件一定要實現介面,否則不能用動態代理