国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

【轉載】Java 動態代理

2019-11-15 00:03:15
字體:
來源:轉載
供稿:網友
【轉載】java 動態代理Java 動態代理

本文為Android 開源項目源碼解析公共技術點中的 動態代理 部分項目地址:Jave PRoxy,分析的版本:openjdk 1.6,Demo 地址:Proxy Demo分析者:Caij,校對者:Trinea,校對狀態:完成

1. 相關概念1.1 代理

在某些情況下,我們不希望或是不能直接訪問對象 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相應函數,并且隱藏了ClassoperateMethod3()函數。

靜態代理中代理類和委托類也常常繼承同一父類或實現同一接口。

1.3 動態代理

代理類在程序運行前不存在、運行時由程序動態生成的代理方式稱為動態代理。

Java 提供了動態代理的實現方式,可以在運行時刻動態生成代理類。這種代理方式的一大好處是可以方便對代理類的函數做統一或特殊處理,如記錄所有函數執行時間、所有函數執行前添加驗證判斷、對某個特殊函數進行特殊操作,而不用像靜態代理方式那樣需要修改每個函數。

靜態代理比較簡單,本文上面已簡單介紹,下面重點介紹動態代理

2. 動態代理實例實現動態代理包括三步:

(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,原因會后續在微博公布。

2.2. 實現 InvocationHandler 接口
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表示代理對象被調用的函數的參數。

調用代理對象的每個函數實際最終都是調用了InvocationHandlerinvoke函數。這里我們在invoke實現中添加了開始結束計時,其中還調用了委托類對象target的相應函數,這樣便完成了統計執行時間的需求。invoke函數中我們也可以通過對method做一些判斷,從而對某些函數特殊處理。

2.3. 通過 Proxy 類靜態函數生成代理對象
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(…)函數新建了一個代理對象,實際代理類就是在這時候動態生成的。我們調用該代理對象的函數就會調用到timingInvocationHandlerinvoke函數(是不是有點類似靜態代理),而invoke函數實現中調用委托類對象new OperateImpl()相應的 method(是不是有點類似靜態代理)。

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

loader表示類加載器interfaces表示委托類的接口,生成代理類時需要實現這些接口hInvocationHandler實現類對象,負責連接代理類和委托類的中間類

我們可以這樣理解,如上的動態代理實現實際是雙層的靜態代理,開發者提供了委托類 B,程序動態生成了代理類 A。開發者還需要提供一個實現了InvocationHandler的子類 C,子類 C 連接代理類 A 和委托類 B,它是代理類 A 的委托類,委托類 B 的代理類。用戶直接調用代理類 A 的對象,A 將調用轉發給委托類 C,委托類 C 再將調用轉發給它的委托類 B。

3. 動態代理原理

實際上面最后一段已經說清了動態代理的真正原理。我們來仔細分析下

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 的代理類。

3.2. 生成動態代理類原理

以下針對 Java 1.6 源碼進行分析,動態代理類是在調用Proxy.newProxyInstance(…)函數時生成的。

(1). 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作為代理類構造函數入參新建代理類對象。

(2). getProxyClass(…)

函數代碼及解釋如下(省略了原英文注釋):

/** * 得到代理類,不存在則動態生成 * @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;}

函數主要包括三部分:

  • 入參 interfaces 檢驗,包含是否在入參指定的 ClassLoader 內、是否是 Interface、interfaces 中是否有重復
  • 以接口名對應的 List 為 key 查找代理類,如果結果為:
    • 弱引用,表示代理類已經在緩存中;
    • pendingGenerationMarker 對象,表示代理類正在生成中,等待生成完成返回;
    • null 表示不在緩存中且沒有開始生成,添加標記到緩存中,繼續生成代理類。
  • 如果代理類不存在調用ProxyGenerator.generateProxyClass(…)生成代理類并存入緩存,通知在等待的緩存。

函數中幾個注意的地方:

  • 代理類的緩存 key 為接口名對應的 List,接口順序不同表示不同的 key 即不同的代理類。
  • 如果 interfaces 中存在非 public 的接口,則所有非 public 接口必須在同一包下面,后續生成的代理類也會在該包下面。
  • 代理類如果在 ClassLoader 中已經存在的情況沒有做處理。
  • 可以開啟 System Properties 的sun.misc.ProxyGenerator.saveGeneratedFiles開關,保存動態類到目的地址。

Java 1.7 的實現略有不同,通過getProxyClass0(…)函數實現,實現中調用代理類的緩存,判斷代理類在緩存中是否已經存在,存在直接返回,不存在則調用proxyClassCachevalueFactory屬性進行動態生成,valueFactoryapply函數與上面的getProxyClass(…)函數邏輯類似。

4. 使用場景4.1 J2EE Web 開發中 Spring 的 AOP(面向切面編程) 特性

作用:目標函數之間解耦。比如在 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 源碼解析完成。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 凤台县| 开封县| 汶川县| 湖南省| 额敏县| 左权县| 怀远县| 上蔡县| 铁岭县| 安化县| 瑞昌市| 呈贡县| 砚山县| 临澧县| 介休市| 边坝县| 昌图县| 崇阳县| 东至县| 肃北| 伊春市| 化隆| 东丰县| 涪陵区| 犍为县| 大足县| 徐汇区| 许昌市| 九寨沟县| 闽清县| 吴江市| 辽阳市| 尖扎县| 乡城县| 始兴县| 泽普县| 绥滨县| 湘潭县| 荆州市| 广昌县| 霞浦县|