使用 JDK 動態代理

如何使用 JDK 動態代理

使用 JDK 的動態代理,需要完成以下兩步:
- 編寫服務接口及其實現
- 編寫代理類,代理類中提供綁定方法和代理方法

接下來我們通過代碼一步步完成一個動態代理的示例。
編寫接口

package com.shawearn.proxy.service;

/**
 * Created by Shawearn on 2017/8/1.
 */
public interface MyService {
    void sayHello(String name);
}

編寫接口實現類

package com.shawearn.proxy.service.impl;

import com.shawearn.proxy.service.MyService;

/**
 * Created by Shawearn on 2017/8/1.
 */
public class MyServiceImpl implements MyService {
    @Override
    public void sayHello(String name) {
        System.out.println("Hello " + name + " !");
    }
}

編寫代理類,代理類需要實現 java.lang.reflect.InvocationHandler 接口

package com.shawearn.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 代理類;
 * <p>
 * Created by Shawearn on 2017/8/1.
 */
public class MyServiceProxy implements InvocationHandler {

    private Object target;

    /**
     * 構造方法,綁定對象;
     *
     * @param target
     */
    public MyServiceProxy(Object target) {
        super();
        this.target = target;
    }

    /**
     * 獲取代理對象;
     *
     * @return
     */
    public Object getProxyInstance() {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    /**
     * 代理方法;
     *
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("########## JDK 動態代理示例 ##########");
        System.out.println("開始調用被代理對象方法:" + method.getName());
        // 反射調用被代理對象中的方法;
        Object result = method.invoke(target, args);
        System.out.println("已經調用被代理對象方法:" + method.getName());
        return result;
    }
}

編寫測試方法

package com.shawearn.proxy;

import com.shawearn.proxy.service.MyService;
import com.shawearn.proxy.service.impl.MyServiceImpl;

/**
 * Created by Shawearn on 2017/8/1.
 */
public class Main {
    public static void main(String[] args) {
        MyServiceProxy myServiceProxy = new MyServiceProxy(new MyServiceImpl());
        MyService proxy = (MyService) myServiceProxy.getProxyInstance();
        proxy.sayHello("Shawearn");
    }
}

運行結果:

########## JDK 動態代理示例 ##########
開始調用被代理對象方法:sayHello
Hello Shawearn !
已經調用被代理對象方法:sayHello

代碼說明

上面的代碼中 MyService 和 MyServiceImpl 只是我們的接口及接口實現類,關鍵在 MyServiceProxy 類中,下面我們以 main 主方法爲起點,一步步描述我們是如何實現代理的。
首先我們需要新建 MyServiceProxy 對象,新建對象時需要傳入參數告知 MyServiceProxy 代理哪一個實例對象:

MyServiceProxy myServiceProxy = new MyServiceProxy(new MyServiceImpl());

調用 getProxyInstance() 方法獲取到代理對象:

MyService proxy = (MyService) myServiceProxy.getProxyInstance();

因爲我們綁定了 MyServiceImpl 的實例對象,而 MyServiceImpl 實現了 MyService 接口,所以我們可以直接把 getProxyInstance() 方法返回的結果強轉爲 MyService 類型。
此時對程序打斷點,我們能看到 proxy 對象其實並不是真正的 MyServiceImpl 類型,而是如下圖所示的一個代理對象

proxy對象

當我們調用 proxy 對象的 sayHello() 方法時

proxy.sayHello("Shawearn");

程序並不會直接進入 MyServiceImpl 中的 sayHello() 方法,而是先進入 MyServiceProxy 中的 invoke() 方法,invoke() 是我們實現 java.lang.reflect.InvocationHandler 接口時所必須實現的方法,該方法有三個參數:
- proxy 被調用方法所在的代理對象

可以通過下面斷點的截圖看到 proxy 信息

invoke方法中的proxy參數

  • method 代理對象被調用的方法所對應的接口方法的方法實例(原文:the Method instance corresponding to the interface method invoked on the proxy instance.)

可以通過下面斷點的截圖看到 method 信息
invoke方法中的method參數
- args 被調用方法的參數值

可以通過下面斷點截圖幫助理解
invoke方法中的args參數
當程序進入到 MyServiceProxy 的 invoke() 方法後,通過反射調用到被代理對象中對應的方法,在本例中即 MyServiceImpl 中的 sayHello() 方法。

// 反射調用被代理對象中的方法;
Object result = method.invoke(target, args);

至此,我們便已經成功使用了 JDK 的動態代理,至於 JDK 底層如何實現動態代理,我們後面再做討論。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章