JDK動態代理實現簡單AOP
JDK 動態代理是 java 反射的一個重要特性。它在某種方面為 java 提供了動態性的特性,給應用帶來了無限的空間。大名鼎鼎的 Hessian 、 Spring AOP 基於動態代理實現。本文將簡單的介紹 JDK 動態代理使用。
關於代理模式
代理模式是非常常用的一種設計模式,在我們的應用中經常被使用。一般情境是,我們有一個現成的類,它的功能比較的完善了,但是還是存在某些欠缺,這個時候我們需要去擴充一些新的功能,但又不想去重造輪子,這個時候可以使用代理類來替代原來的目標類,通過組合的模式,增加一種為目標類增加一些額外的功能。
代理模式的類結構圖一般如下:
圖1:代理模式類結構圖
圖2:代理模式順序圖表
JDK 動態代理
所謂 JDK 動態代理就是在運行時動態產生紅色的代理類的過程。和靜態代理不一樣的地方是靜態代理在編譯期就要完成代理類 的實現。那麼動態代理是如何?的呢。
根據下面的例子,一步一步來看。
假設有這樣一個介面 Speak:
package proxy; public interface Speak { public void sayHello(); }
實作類別 PeopleSpeak.
package proxy; public class PersonSpeak implements Speak { public void sayHello() { System.out.println("hello"); } }
當我們去調用 PersonSpeak 的 sayHello 的時候,很顯然是輸出 hello 。現在我們需要通過動態代理在運行時動態產生 Speak 的代理類,並在調用 sayHello 方法的前後做一些輸出。完成類似 aop 的功能。
根據以上的思路,我們需要有一個 PersonSpeak 的代理類,它持了 PersonSpeak 的對象,這樣就能很方便的完成這個任務。如果用靜態代理實現顯然是很簡單,那麼用動態代理如何?呢。
這個時候我們可以使用 java 反射裡的 Proxy 類。此類是 JDK 動態代理的核心類。 Proxy 類擁有一個在運行期動態建立類的功能。動態去建立一個 Speak 的子類,同時該子類持有 PersonSpeak 類的一個執行個體,該子類的功能就是實現上述第一部分代理類的功能。
關於 java 反射的 Method 、 Field 、 Class 、 Construtor 等這裡不做介紹,重點介紹 Proxy和 InvocationHandler 類。
Proxy和InvocationHandler 類簡介
Proxy 類提供了非常重要的方法,該方法能夠動態產生幾個介面的實作類別。具體的實現過程由 ProxyGenerator 類來實現,這是比較低層的一個類,這裡不做過多描述。同時該動態類需要持有一個實現了 InvocationHandler 介面的子類對象。 InvocationHandler 介面的子類對象是我們目標類的又一層代理。對於介面裡面的每個方法實現,都是通過調用 InvocationHandler 介面的子類對象的反射調用。也就是說動態代理類實際上是 InvocationHandler 子類對象的一個代理。那麼 InvocationHandler 子類對象有時我們目標類的代理。通過這樣層層代理我們就能實現上述的功能了。這句話可能不是很好理解,但是確實就是這麼實現的,也許通過下面的圖就能更好的理解:
圖3:JDK動態代理調用過程
可以從上圖看出來, JDK 動態反射並不是那麼簡單的一層代理,而是通過層層代理,最終通過 Method 的反射來調用目標對象的方法,而 aop 的實現可以放在 InvocationHandler 的是實作類別裡。
那麼根據上述關於動態代理的簡介,要實現 PersonSpeak 的 aop ,需要做兩件事情
1. 實現一個持有 Speak 對象的 InvocationHandler 子類,改子類通過 Mechod 反射調用 PersonSpeak 的方法,並在調用方法前後實現 aop 功能。
2. 實現一個能動態建立 Speak 子類的代理類工廠,改工廠能動態建立 Speak 子類。
具體實現如下:
1. SpeakInvocationHandler ( 實現 InvocationHandler 介面 )
可以看出該類的 invoke 方法實現 aop 的關鍵,所有方法都是通過 invoke 來調用, invoke 內部通過反射來調用目標對象 target ,同時在調用前後實現了 aop 。
package proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class SpeakInvocationHandler implements InvocationHandler { public Object target; SpeakInvocationHandler(Object target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(method.getName() + " invoked!"); Object result = method.invoke(target, args); System.out.println(method.getName() + " return!"); return result; } }
2. ProxyFactory
package proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; public class ProxyFactory { Class cls; InvocationHandler h; public ProxyFactory(String className, InvocationHandler h) { try { cls = Class.forName(className); this.h = h; } catch (ClassNotFoundException e) { e.printStackTrace(); } } public Object createProxyObject() { return Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), h); } }
ProxyFactory 主要是通過反射的 Proxy 類動態去建立介面的子類,同時該子類是 InvocationHandler 的一個代理,所有的方法實現,都是通過 InvocationHandler 代理來調用。
最後來看下 Main 函數:
package proxy; import java.lang.reflect.InvocationHandler; public class ProxyTest { public static void main(String args[]) { Speak s = new PersonSpeak(); InvocationHandler h = new SpeakInvocationHandler(s); ProxyFactory proxyFactory = new ProxyFactory(PersonSpeak.class.getName(), h); Speak speakProxy = (Speak) proxyFactory.createProxyObject(); speakProxy.sayHello(); } }
得到的輸出結果如下:
sayHello invoked!
hello
sayHello return!
從輸出結果可以看出紅色的輸出部分正是我們通過動態代理來實現的。通過如上的過程完成了正果動態代理的過程。
除了實現 AOP ,動態代理還可以用於實現遠程服務代理。比如說 Hessian 就是通過動態建立遠程服務代理類來調用遠程服務,從而使應用對遠程服務透明化。 Hessian 調用過程如下:
圖4:Hessian遠程服務調用過程
總結
JDK 動態代理是 java 反射的一個非常重要的特性, JDK 動態代理類還可以用來作為 SOAP 的遠程服務代理類,總之它在某種程度上提供了 java 的動態性的特點,為應用提供了很大的靈活性。