JDK動態代理
在JDK 1.3以後提供了動態代理的技術,允許開發者在運行期創建接口的代理實例。在Sun剛推出動態代理時,還很難想象它有多大的實際用途,現在我們終於發現動態代理是實現AOP的絕好底層技術。
JDK的動態代理主要涉及到java.lang.reflect包中的兩個類:Proxy和InvocationHandler。其中 InvocationHandler是一個接口,可以通過實現該接口定義橫切邏輯,在並通過反射機制調用目標類的代碼,動態將橫切邏輯和業務邏輯編織在一 起。
而Proxy爲InvocationHandler實現類動態創建一個符合某一接口的代理實例。這樣講一定很抽象,我們馬上着手動用Proxy和InvocationHandler這兩個魔法戒對上一節中的性能監視代碼進行AOP式的改造。
首先,我們從業務類ForumServiceImpl 中刪除性能監視的橫切代碼,使ForumServiceImpl只負責具體的業務邏輯,如所示:
代碼清單 5 ForumServiceImpl:移除性能監視橫切代碼
在代碼清單 5中的①和②處,原來的性能監視代碼被移除了,我們只保留了真正的業務邏輯。
從業務類中移除的橫切代碼當然還得找到一個寄居之所,InvocationHandler就是橫切代碼的家園樂土,我們將性能監視的代碼安置在PerformaceHandler中,如代碼清單 6所示:
代碼清單 6 PerformaceHandler
粗體部分的代碼爲性能監視的橫切代碼,我們發現,橫切代碼只出現一次,而不是原來那樣星灑各處。大家注意②處的method.invoke(),該語句通 過反射的機制調用目標對象的方法,這樣InvocationHandler的invoke(Object proxy, Method method, Object[] args)方法就將橫切代碼和目標業務類代碼編織到一起了,所以我們可以將InvocationHandler看成是業務邏輯和橫切邏輯的編織器。下面,
我們對這段代碼做進一步的說明。
首先,我們實現InvocationHandler接口,該接口定義了一個 invoke(Object proxy, Method method, Object[] args)的方法,proxy是代理實例,一般不會用到;method是代理實例上的方法,通過它可以發起對目標類的反射調用;args是通過代理類傳入 的方法參數,在反射調用時使用。
此外,我們在構造函數裏通過target傳入真實的目標對象,如①處所示,在接口方法invoke(Object proxy, Method method, Object[] args)裏,將目標類實例傳給method.invoke()方法,通過反射調用目標類方法,如②所示。
下面,我們通過Proxy結合PerformaceHandler創建ForumService接口的代理實例,如代碼清單 7所示:
代碼清單 7 TestForumService:創建代理實例
上面的代碼完成了業務類代碼和橫切代碼編織和接口代理實例生成的工作,其中在②處,我們將ForumService實例編織爲一個包含性能監視邏輯的 PerformaceHandler實例,然後在③處,通過Proxy的靜態方法newProxyInstance()爲融合了業務類邏輯和性能監視邏輯 的handler創建一個ForumService接口的代理實例,該方法的第一個入參爲類加載器,第二個入參爲創建的代理實例所要實現的一組接口,第三
個參數是整合了業務邏輯和橫切邏輯的編織器對象。
按照③處的設置方式,這個代理實例就實現了目標業務類的所有接口,也即ForumServiceImpl的ForumService接口。這樣,我們就可以按照調用ForumService接口的實例相同的方式調用代理實例,如④所示。運行以上的代碼,輸出以下的信息:
我們發現,程序的運行效果和直接在業務類中編寫性能監視邏輯的效果一致,但是在這裏,原來分散的橫切邏輯代碼已經被我們抽取到 PerformaceHandler中。當其它業務類(如UserService、SystemService等)的業務方法也需要使用性能監視時,我們 只要按照以上的方式,分別爲它們創建代理對象就可以了。下面,我們用時序圖描述調用關係,進一步代理實例的本質,如圖1所示:
圖 1代理實例的時序圖
我們在上圖中特別使用虛線陰影的方式對通過代理器創建的ForumService實例進行凸顯,該實例內部利用PerformaceHandler整合橫 切邏輯和業務邏輯。調用者調用代理對象的的removeForum()和removeTopic()方法時,上圖的內部調用時序清晰地告訴了我們實際上所 發生的一切。