如何使用 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 對象的 sayHello() 方法時
proxy.sayHello("Shawearn");
程序並不會直接進入 MyServiceImpl 中的 sayHello() 方法,而是先進入 MyServiceProxy 中的 invoke() 方法,invoke() 是我們實現 java.lang.reflect.InvocationHandler 接口時所必須實現的方法,該方法有三個參數:
- proxy 被調用方法所在的代理對象
可以通過下面斷點的截圖看到 proxy 信息
- method 代理對象被調用的方法所對應的接口方法的方法實例(原文:the Method instance corresponding to the interface method invoked on the proxy instance.)
可以通過下面斷點的截圖看到 method 信息
- args 被調用方法的參數值
可以通過下面斷點截圖幫助理解
當程序進入到 MyServiceProxy 的 invoke() 方法後,通過反射調用到被代理對象中對應的方法,在本例中即 MyServiceImpl 中的 sayHello() 方法。
// 反射調用被代理對象中的方法;
Object result = method.invoke(target, args);
至此,我們便已經成功使用了 JDK 的動態代理,至於 JDK 底層如何實現動態代理,我們後面再做討論。