JDK 1.3以後,java提供了動態代理的技術,允許開發者在運行初期創建接口的代理實例。所謂代理即對某一實例的增強,對象在實例化的時候得到增強,增強過的實例即爲代理實例,代理實例往往在運行的時候被動態增強。
JDK的動態代理主要涉及到java.lang.reflect包中的兩個類:Proxy和InvocationHandler。其中InvocationHandler是一個接口,可以通過實現該接口定義橫切邏輯,並通過反射機制調用目標類代碼,動態地將橫切邏輯和業務邏輯編織在一起。
而Proxy利用InvocationHandler動態創建一個符合某一接口的實例,生成目標類的代理對象。下面通過一組實例演示如何使用JDK的動態代理。
FormService.java
public interface FormService { /** * 模擬論壇話題被刪除 * @param topicId */ public void removeTopic(int topicId); /** * 模擬論壇被刪除 * @param forumId */ public void removeForum(int forumId); }
FormServiceImpl.java
public class FormServiceImpl implements FormService { @Override public void removeTopic(int topicId) { System.out.println("模擬刪除Topic記錄:" + topicId); try { Thread.sleep(20); } catch (InterruptedException e) { throw new RuntimeException(); } } @Override public void removeForum(int forumId) { System.out.println("模擬刪除Topic記錄:" + forumId); try { Thread.sleep(40); } catch (InterruptedException e) { throw new RuntimeException(); } } }
PerformanceHandler.java
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class PerformanceHandler implements InvocationHandler { private Object target; public PerformanceHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] params) throws Throwable { String methodName = target.getClass().getName() + "." + method.getName(); System.out.println("對" + methodName + "方法進行性能檢測。"); long start = System.currentTimeMillis(); Object obj = method.invoke(target, params); long end = System.currentTimeMillis(); System.out.println(methodName +"方法用時:" + (end - start) + "毫秒。"); return obj; } }
測試類TestForProxy.java
import java.lang.reflect.Proxy; public class TestForProxy { public static void main(String[] args) { FormService forumService = new FormServiceImpl(); PerformanceHandler handler = new PerformanceHandler(forumService); FormService proxy = (FormService) Proxy.newProxyInstance( forumService.getClass().getClassLoader(), forumService.getClass().getInterfaces(),handler); proxy.removeForum(10); proxy.removeTopic(1012); } }
程序運行運行結果:
對org.proxy.jdkproxy.FormServiceImpl.removeForum方法進行性能檢測。
模擬刪除Topic記錄:10
org.proxy.jdkproxy.FormServiceImpl.removeForum方法用時:40毫秒。
對org.proxy.jdkproxy.FormServiceImpl.removeTopic方法進行性能檢測。
模擬刪除Topic記錄:1012
org.proxy.jdkproxy.FormServiceImpl.removeTopic方法用時:20毫秒。
在以上代碼中,我們實現了InvocationHandler接口,該接口定義了一個invoke(Object proxy,Method method,Object[] params)的方法,proxy是最終生成的代理實例,一般不會用到;method是被代理目標實例的某個具體方法,通過他可以發起目標實例方法的反射調用;params是通過被代理實例某一具體方法的入參,在方法反射時使用它。
注意:JDK的動態代理是針對接口的代理,即增強目標類必須是實現了某一個或多個接口的類,其被增強的方法也必須是接口中的方法,之所以稱之爲動態代理是因爲對實例的代理髮生在運行期,另外代理還可以發生在編譯期和裝載期(需要特殊的編譯器和裝載器)。