1. 相關概念1.1 代理本文為Android 開源項目源碼解析公共技術點中的 動態代理 部分項目地址:Jave PRoxy,分析的版本:openjdk 1.6,Demo 地址:Proxy Demo分析者:Caij,校對者:Trinea,校對狀態:完成
在某些情況下,我們不希望或是不能直接訪問對象 A,而是通過訪問一個中介對象 B,由 B 去訪問 A 達成目的,這種方式我們就稱為代理。這里對象 A 所屬類我們稱為委托類,也稱為被代理類,對象 B 所屬類稱為代理類。代理優點有:
根據程序運行前代理類是否已經存在,可以將代理分為靜態代理和動態代理。
1.2 靜態代理代理類在程序運行前已經存在的代理方式稱為靜態代理。通過上面解釋可以知道,由開發人員編寫或是編譯器生成代理類的方式都屬于靜態代理,如下是簡單的靜態代理實例:
class ClassA { public void OperateMethod1() {}; public void operateMethod2() {}; public void operateMethod3() {};}public class ClassB { private ClassA a; public ClassB(ClassA a) { this.a = a; } public void operateMethod1() { a.operateMethod1(); }; public void operateMethod2() { a.operateMethod2(); }; // not export operateMethod3()}上面ClassA是委托類,ClassB是代理類,ClassB中的函數都是直接調用ClassA相應函數,并且隱藏了Class的operateMethod3()函數。
靜態代理中代理類和委托類也常常繼承同一父類或實現同一接口。
1.3 動態代理代理類在程序運行前不存在、運行時由程序動態生成的代理方式稱為動態代理。
Java 提供了動態代理的實現方式,可以在運行時刻動態生成代理類。這種代理方式的一大好處是可以方便對代理類的函數做統一或特殊處理,如記錄所有函數執行時間、所有函數執行前添加驗證判斷、對某個特殊函數進行特殊操作,而不用像靜態代理方式那樣需要修改每個函數。
靜態代理比較簡單,本文上面已簡單介紹,下面重點介紹動態代理。
(1). 新建委托類;(2). 實現InvocationHandler接口,這是負責連接代理類和委托類的中間類必須實現的接口;(3). 通過Proxy類新建代理類對象。
下面通過實例具體介紹,假如現在我們想統計某個類所有函數的執行時間,傳統的方式是在類的每個函數前打點統計,動態代理方式如下:
2.1 新建委托類public interface Operate { public void operateMethod1(); public void operateMethod2(); public void operateMethod3();}public class OperateImpl implements Operate { @Override public void operateMethod1() { System.out.println("Invoke operateMethod1"); sleep(110); } @Override public void operateMethod2() { System.out.println("Invoke operateMethod2"); sleep(120); } @Override public void operateMethod3() { System.out.println("Invoke operateMethod3"); sleep(130); } private static void sleep(long millSeconds) { try { Thread.sleep(millSeconds); } catch (InterruptedException e) { e.printStackTrace(); } }}Operate是一個接口,定了了一些函數,我們要統計這些函數的執行時間。OperateImpl是委托類,實現Operate接口。每個函數簡單輸出字符串,并等待一段時間。動態代理要求委托類必須實現了某個接口,比如這里委托類OperateImpl實現了Operate,原因會后續在微博公布。
public class TimingInvocationHandler implements InvocationHandler { private Object target; public TimingInvocationHandler() {} public TimingInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long start = System.currentTimeMillis(); Object obj = method.invoke(target, args); System.out.println(method.getName() + " cost time is:" + (System.currentTimeMillis() - start)); return obj; }}target屬性表示委托類對象。
InvocationHandler是負責連接代理類和委托類的中間類必須實現的接口。其中只有一個
public Object invoke(Object proxy, Method method, Object[] args)函數需要去實現,參數:proxy表示下面2.3 通過 Proxy.newProxyInstance() 生成的代理類對象。method表示代理對象被調用的函數。args表示代理對象被調用的函數的參數。
調用代理對象的每個函數實際最終都是調用了InvocationHandler的invoke函數。這里我們在invoke實現中添加了開始結束計時,其中還調用了委托類對象target的相應函數,這樣便完成了統計執行時間的需求。invoke函數中我們也可以通過對method做一些判斷,從而對某些函數特殊處理。
public class Main { public static void main(String[] args) { // create proxy instance TimingInvocationHandler timingInvocationHandler = new TimingInvocationHandler(new OperateImpl()); Operate operate = (Operate)(Proxy.newProxyInstance(Operate.class.getClassLoader(), new Class[] {Operate.class}, timingInvocationHandler)); // call method of proxy instance operate.operateMethod1(); System.out.println(); operate.operateMethod2(); System.out.println(); operate.operateMethod3(); }}這里我們先將委托類對象new OperateImpl()作為TimingInvocationHandler構造函數入參創建timingInvocationHandler對象;然后通過Proxy.newProxyInstance(…)函數新建了一個代理對象,實際代理類就是在這時候動態生成的。我們調用該代理對象的函數就會調用到timingInvocationHandler的invoke函數(是不是有點類似靜態代理),而invoke函數實現中調用委托類對象new OperateImpl()相應的 method(是不是有點類似靜態代理)。
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)loader表示類加載器interfaces表示委托類的接口,生成代理類時需要實現這些接口h是InvocationHandler實現類對象,負責連接代理類和委托類的中間類
我們可以這樣理解,如上的動態代理實現實際是雙層的靜態代理,開發者提供了委托類 B,程序動態生成了代理類 A。開發者還需要提供一個實現了InvocationHandler的子類 C,子類 C 連接代理類 A 和委托類 B,它是代理類 A 的委托類,委托類 B 的代理類。用戶直接調用代理類 A 的對象,A 將調用轉發給委托類 C,委托類 C 再將調用轉發給它的委托類 B。
實際上面最后一段已經說清了動態代理的真正原理。我們來仔細分析下
3.1 生成的動態代理類代碼下面是上面示例程序運行時自動生成的動態代理類代碼,如何得到這些生成的代碼請見ProxyUtils,查看 class 文件可使用 jd-gui
import com.codekk.java.test.dynamicproxy.Operate;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 Operate{ private static Method m4; private static Method m1; private static Method m5; private static Method m0; private static Method m3; private static Method m2; public $Proxy0(InvocationHandler paramInvocationHandler) throws { super(paramInvocationHandler); } public final void operateMethod1() throws { try { h.invoke(this, m4, null); return; } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final boolean equals(Object paramObject) throws { try { return ((Boolean)h.invoke(this, m1, new Object[] { paramObject })).booleanValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final void operateMethod2() throws { try { h.invoke(this, m5, null); return; } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final int hashCode() throws { try { return ((Integer)h.invoke(this, m0, null)).intValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final void operateMethod3() throws { try { h.invoke(this, m3, null); return; } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final String toString() throws { try { return (String)h.invoke(this, m2, null); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } static { try { m4 = Class.forName("com.codekk.java.test.dynamicproxy.Operate").getMethod("operateMethod1", new Class[0]); m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m5 = Class.forName("com.codekk.java.test.dynamicproxy.Operate").getMethod("operateMethod2", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); m3 = Class.forName("com.codekk.java.test.dynamicproxy.Operate").getMethod("operateMethod3", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); return; } catch (NoSuchMethodException localNoSuchMethodException) { throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); } catch (ClassNotFoundException localClassNotFoundException) { throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); } }}從中我們可以看出動態生成的代理類是以$Proxy為類名前綴,繼承自Proxy,并且實現了Proxy.newProxyInstance(…)第二個參數傳入的所有接口的類。如果代理類實現的接口中存在非 public 接口,則其包名為該接口的包名,否則為com.sun.proxy。其中的operateMethod1()、operateMethod2()、operateMethod3()函數都是直接交給h去處理,h在父類Proxy中定義為
protected InvocationHandler h;即為Proxy.newProxyInstance(…)第三個參數。所以InvocationHandler的子類 C 連接代理類 A 和委托類 B,它是代理類 A 的委托類,委托類 B 的代理類。
以下針對 Java 1.6 源碼進行分析,動態代理類是在調用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 (Object) 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()); }}從中可以看出它先調用getProxyClass(loader, interfaces)得到動態代理類,然后將InvocationHandler作為代理類構造函數入參新建代理類對象。
函數代碼及解釋如下(省略了原英文注釋):
/** * 得到代理類,不存在則動態生成 * @param loader 代理類所屬 ClassLoader * @param interfaces 代理類需要實現的接口 * @return */public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) throws IllegalArgumentException{ if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } // 代理類類對象 Class proxyClass = null; /* collect interface names to use as key for proxy class cache */ String[] interfaceNames = new String[interfaces.length]; Set interfaceSet = new HashSet(); // for detecting duplicates /** * 入參 interfaces 檢驗,包含三部分 * (1)是否在入參指定的 ClassLoader 內 * (2)是否是 Interface * (3)interfaces 中是否有重復 */ for (int i = 0; i < interfaces.length; i++) { String interfaceName = interfaces[i].getName(); Class interfaceClass = null; try { interfaceClass = Class.forName(interfaceName, false, loader); } catch (ClassNotFoundException e) { } if (interfaceClass != interfaces[i]) { throw new IllegalArgumentException( interfaces[i] + " is not visible from class loader"); } if (!interfaceClass.isInterface()) { throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } if (interfaceSet.contains(interfaceClass)) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } interfaceSet.add(interfaceClass); interfaceNames[i] = interfaceName; } // 以接口名對應的 List 作為緩存的 key Object key = Arrays.asList(interfaceNames); /* * loaderToCache 是個雙層的 Map * 第一層 key 為 ClassLoader,第二層 key 為 上面的 List,value 為代理類的弱引用 */ Map cache; synchronized (loaderToCache) { cache = (Map) loaderToCache.get(loader); if (cache == null) { cache = new HashMap(); loaderToCache.put(loader, cache); } } /* * 以上面的接口名對應的 List 為 key 查找代理類,如果結果為: * (1) 弱引用,表示代理類已經在緩存中 * (2) pendingGenerationMarker 對象,表示代理類正在生成中,等待生成完成通知。 * (3) null 表示不在緩存中且沒有開始生成,添加標記到緩存中,繼續生成代理類 */ synchronized (cache) { do { Object value = cache.get(key); if (value instanceof Reference) { proxyClass = (Class) ((Reference) value).get(); } if (proxyClass != null) { // proxy class already generated: return it return proxyClass; } else if (value == pendingGenerationMarker) { // proxy class being generated: wait for it try { cache.wait(); } catch (InterruptedException e) { } continue; } else { cache.put(key, pendingGenerationMarker); break; } } while (true); } try { String proxyPkg = null; // package to define proxy class in /* * 如果 interfaces 中存在非 public 的接口,則所有非 public 接口必須在同一包下面,后續生成的代理類也會在該包下面 */ for (int i = 0; i < interfaces.length; i++) { int flags = interfaces[i].getModifiers(); if (!Modifier.isPublic(flags)) { String name = interfaces[i].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, proxyPkg = ""; // use the unnamed package } { // 得到代理類的類名,jdk 1.6 版本中缺少對這個生成類已經存在的處理。 long num; synchronized (nextUniqueNumberLock) { num = nextUniqueNumber++; } String proxyName = proxyPkg + proxyClassNamePrefix + num; // 動態生成代理類的字節碼 // 最終調用 sun.misc.ProxyGenerator.generateClassFile() 得到代理類相關信息寫入 DataOutputStream 實現 byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces); try { // native 層實現,虛擬機加載代理類并返回其類對象 proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { throw new IllegalArgumentException(e.toString()); } } // add to set of all generated proxy classes, for isProxyClass proxyClasses.put(proxyClass, null); } finally { // 代理類生成成功則保存到緩存,否則從緩存中刪除,然后通知等待的調用 synchronized (cache) { if (proxyClass != null) { cache.put(key, new WeakReference(proxyClass)); } else { cache.remove(key); } cache.notifyAll(); } } return proxyClass;}函數主要包括三部分:
ProxyGenerator.generateProxyClass(…)生成代理類并存入緩存,通知在等待的緩存。函數中幾個注意的地方:
sun.misc.ProxyGenerator.saveGeneratedFiles開關,保存動態類到目的地址。Java 1.7 的實現略有不同,通過getProxyClass0(…)函數實現,實現中調用代理類的緩存,判斷代理類在緩存中是否已經存在,存在直接返回,不存在則調用proxyClassCache的valueFactory屬性進行動態生成,valueFactory的apply函數與上面的getProxyClass(…)函數邏輯類似。
作用:目標函數之間解耦。比如在 Dao 中,每次數據庫操作都需要開啟事務,而且在操作的時候需要關注權限。一般寫法是在 Dao 的每個函數中添加相應邏輯,造成代碼冗余,耦合度高。使用動態代理前偽代碼如下:
Dao { insert() { 判斷是否有保存的權限; 開啟事務; 插入; 提交事務; } delete() { 判斷是否有刪除的權限; 開啟事務; 刪除; 提交事務; }}使用動態代理的偽代碼如下:
// 使用動態代理,組合每個切面的函數,而每個切面只需要關注自己的邏輯就行,達到減少代碼,松耦合的效果invoke(Object proxy, Method method, Object[] args) throws Throwable { 判斷是否有權限; 開啟事務; Object ob = method.invoke(dao, args); 提交事務; return ob; }4.2 基于 REST 的 Android 端網絡請求框架 Retrofit作用:簡化網絡請求操作。一般情況下每個網絡請求我們都需要調用一次HttpURLConnection或者HttpClient進行請求,或者像Volley一樣丟進等待隊列中,Retrofit 極大程度簡化了這些操作,示例代碼如下:
public interface GitHubService { @GET("/users/{user}/repos") List<Repo> listRepos(@Path("user") String user);}RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("https://api.github.com") .build();GitHubService service = restAdapter.create(GitHubService.class);以后我們只需要直接調用
List<Repo> repos = service.listRepos("octocat");即可開始網絡請求,Retrofit的原理就是基于動態代理,它同時用到了注解的原理,本文不做深入介紹,具體請等待Retrofit 源碼解析完成。
新聞熱點
疑難解答