網頁

2020/11/17

Java Proxy 動態代理範例 Dynamic Proxy simple example

Java Proxy動態代理簡單範例。

建議先了解什麼是代理模式(Proxy Pattern)才能理解Java的Proxy動態代理,因為背後觀念一樣。

Java Proxy動態代理是基於反射(Reflection)來實現,與一般代理的差別在於其不用事先定義好原物件的代理類別,允許在執行期間(runtime)才生成代理物件供客戶端使用。

使用Java動態代理的步驟有三;

  1. 被代理對象必須提供介面。
  2. 實作InvocationHandler提供代理要處理的邏輯。
  3. 使用Proxy生成代理物件。

SubjectRealSubject實作的介面。

Subject

public interface Subject {

    void service();

}

RealSubject為原物件,Thread.sleep()模擬執行時耗費的時間。

RealSubject

public class RealSubject implements Subject {

    @Override
    public void service() {
        try {
            Thread.sleep(2000L);
            System.out.println("Do something...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

LogProxyInvocationHandler實作InvocationHandler介面,在invoke()方法實作代理物件的邏輯,並轉調用原物件的方法。

LogProxyInvocationHandler

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Date;

public class LogProxyInvocationHandler implements InvocationHandler {

    private final Object realSubject; // 被代理的原物件

    public LogProxyInvocationHandler(Object realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(method.getName() + " 開始於:" + new Date());

        Object result = method.invoke(realSubject, args); // 調用原物件的方法

        System.out.println(method.getName() + " 結束於:" + new Date());
        return result;
    }
}

Client為客戶端,透過Proxy.newProxyInstance()動態產生代理物件,回傳物件型態為傳入的原物件介面參數。

Client

import java.lang.reflect.Proxy;

public class Client {

    public static void main(String[] args) {

        Subject realSubject = new RealSubject();

        Subject proxy = (Subject) Proxy.newProxyInstance(
                realSubject.getClass().getClassLoader(), // 被代理對象的ClassLoader
                realSubject.getClass().getInterfaces(), // 被代理對象的實作介面
                new LogProxyInvocationHandler(realSubject) // InvocationHandler實作
        );

        proxy.service();
    }

}

執行結果如下。

service 開始於:Wed Nov 18 15:32:15 CST 2020
Do something...
service 結束於:Wed Nov 18 15:32:17 CST 2020


代理物件生成的邏輯也可封裝在代理處理類別LogProxyInvocationHandler中。

LogProxyInvocationHandler

public class LogProxyInvocationHandler implements InvocationHandler {

    private final Object realSubject; // 被代理的原物件

    private LogProxyInvocationHandler(Object realSubject) {
        this.realSubject = realSubject;
    }

    // 生成代理
    public static Object newInstance(Object realSubject) {
        return Proxy.newProxyInstance(
                realSubject.getClass().getClassLoader(),
                realSubject.getClass().getInterfaces(),
                new LogProxyInvocationHandler(realSubject)
        );
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(method.getName() + " 開始於:" + new Date());

        Object result = method.invoke(realSubject, args); // 調用原物件的方法

        System.out.println(method.getName() + " 結束於:" + new Date());
        return result;
    }
}

Client

public class Client {

    public static void main(String[] args) {

        Subject realSubject = new RealSubject();

        Subject proxy = (Subject) LogProxyInvocationHandler.newInstance(realSubject);

        proxy.service();
    }

}

以上即為Java動態代理的簡單範例。


沒有留言:

張貼留言