Java進階--Java動態代理


JDK version: 1.8

動態代理中所說的“動態”, 是針對使用Java代碼實際編寫了代理類的“靜態”代理而言的, 它的優勢不在於省去了編寫代理類那一點編碼工作量, 
而是實現了可以在原始類和接口還未知的時候, 就確定代理類的代理行為,當代理類與原始類脫離直接聯系后, 就可以很靈活地重用於不同的應用場景之中。

  • 目前Java開發包中包含了對動態代理的支持, 但是其實現只支持對接口的的實現。其實現主要通過 java.lang.reflect.Proxy 類和 java.lang.reflect.InvocationHandler 接口。
  • Proxy 類主要用來獲取動態代理對象, InvocationHandler 接口用來約束調用者實現。
  • 動態代理是很多框架和技術的基礎, spring 的 AOP 實現就是基於動態代理實現的。

Proxy類
Proxy 提供用於創建動態代理類實例的靜態方法, 它還是與之創建的所有動態代理類的超類。
介紹一下 Proxy 類中最常用的方法 java.lang.reflect.Proxy#newProxyInstance,

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
    ...
    return cons.newInstance(new Object[]{h});
    ...
}

該方法返回動態代理類實例. 該方法有三個參數

  • loader: the class loader to define the proxy class. 與原始類的類加載器一致
  • interfaces: the list of interfaces for the proxy class to implement. 原始類實現的接口
  • h: InvocationHandler 實例, 動態代理類實例會調用 InvocationHandler 實例的 invoke 方法

InvocationHandler接口
InvocationHandler 接口中只有一個方法.

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

該方法有三個參數

  • proxy: 動態代理類的實例
  • method: 原始類的 Method 實例
  • args: 被代理 Method 需要的參數

下面通過編寫一個簡單地動態代理類舉例說明:

 1 public class DomainProxyTest {
 2 
 3     interface IGreet {
 4 
 5         void sayHello();
 6 
 7         void sayHi();
 8     }
 9 
10     static class Greet implements IGreet {
11 
12         @Override
13         public void sayHello() {
14             System.out.println("Hello World!");
15         }
16 
17         @Override
18         public void sayHi() {
19             System.out.println("Hi there!");
20         }
21     }
22 
23     static class DynamicProxy implements java.lang.reflect.InvocationHandler {
24 
25         Object originalObj;
26 
27         Object bind(Object originalObj) {
28             this.originalObj = originalObj;
29             return java.lang.reflect.Proxy
30                 .newProxyInstance(originalObj.getClass().getClassLoader(), originalObj.getClass().getInterfaces(), this);
31         }
32 
33         @Override
34         public Object invoke(Object proxy, java.lang.reflect.Method method, Object[] args) throws Throwable {
35             System.out.println("welcome!");
36             return method.invoke(originalObj, args);
37         }
38     }
39 
40     public static void main(String[] args) {
41         System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
42         IGreet greet = (IGreet) new DynamicProxy().bind(new Greet());
43         greet.sayHello();
44         greet.sayHi();
45     }
46 }

編譯 Java 文件:

javac DomainProxyTest.java

 執行 class 文件:

 

 發現生成了一個名稱為 $Proxy0.class 的文件, 該文件就是 Java 動態生成的代理類. 反編譯看一下其內容:

import DomainProxyTest.IGreet;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

final class $Proxy0 extends Proxy implements IGreet {
    private static Method m1;
    private static Method m3;
    private static Method m4;
    private static Method m2;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void sayHello() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void sayHi() throws  {
        try {
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("DomainProxyTest$IGreet").getMethod("sayHello");
            m4 = Class.forName("DomainProxyTest$IGreet").getMethod("sayHi");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

代碼邏輯很簡單, 呼應了文章上面對動態代理的描述.

 

點擊鏈接加入QQ群: 282575808互聯網技術交流群】:https://jq.qq.com/?_wv=1027&k=Iw86cqY6


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM