Spring AOP中的JDK動態代理
一、關於靜態代理和動態代理的概念1 代理模式是常用的Java設計模式,它的特徵是代理類與委託類有同樣的介面,代理類主要負責為委託類預處理訊息、過濾訊息、把訊息轉發給委託類,以及事後處理訊息等。代理類與委託類之間通常會存在關聯關係,一個代理類的物件與一個委託類的物件關聯,代理類的物件本身並不真正實現服務,而是通過呼叫委託類的物件的相關方法,來提供特定的服務。按照代理類的建立時期,代理類可分為兩種。 靜態代理類:由程式設計師建立或由特定工具自動生成原始碼,再對其編譯。在程式執行前,代理類的.class檔案就已經存在了。 動態代理類:在程式執行時,運用反射機制動態建立而成。 靜態代理通常只代理一個類,動態代理是代理一個介面下的多個實現類。 靜態代理事先知道要代理的是什麼,而動態代理不知道要代理什麼東西,只有在執行時才知道。 動態代理是實現JDK裡的InvocationHandler介面的invoke方法,但注意的是代理的是介面,也就是你的業務類必須要實現介面,通過Proxy裡的newProxyInstance得到代理物件。 還有一種動態代理CGLIB,代理的是類,不需要業務類繼承介面,通過派生的子類來實現代理。通過在執行時,動態修改位元組碼達到修改類的目的。 2、JDK動態代理 JDK的動態代理主要涉及java.lang.reflect包中的兩個類:Proxy類和InvocationHandler。其中,InvocationHandler是一個介面,可以通過實現介面定義橫切邏輯,並通過反射機制呼叫目標類的程式碼,動態地將橫切邏輯和業務邏輯編織在一起。 而Proxy利用InvocationHandler動態建立一個符合某一介面的例項,生成目標類的代理物件。接下來用一個例子說明: (1)建立一個業務類ForumService
package com.changmin.proxy;
public interface ForumService {
void removeTopic(int topicId);
void removeForum(int forumId);
}
(2)建立一個帶有橫切邏輯的例項
package com.changmin.proxy; public class ForumServiceImpl implements ForumService { public void removeTopic(int topicId) { //PerformanceMonitor.begin("com.changmin.proxy.ForumServiceImpl.removeTopic"); System.out.println("模擬刪除Topic記錄:"+topicId); try { Thread.currentThread().sleep(20); } catch (Exception e) { throw new RuntimeException(e); } //PerformanceMonitor.end(); } public void removeForum(int forumId) { //PerformanceMonitor.begin("com.changmin.proxy.ForumServiceImpl.removeForum"); System.out.println("模擬刪除Forum記錄:"+forumId); try { Thread.currentThread().sleep(40); } catch (Exception e) { throw new RuntimeException(e); } //PerformanceMonitor.end(); } }
每一個Service類和每個業務方法體前後都執行相同的程式碼邏輯:方法呼叫前啟動PerformanceMonitor.begin();方法呼叫後啟動PerformanceMonitor.end()結束效能監視。 (3)建立效能監視的實現類PerformanceMonitor
package com.changmin.proxy; public class PerformanceMonitor { private static ThreadLocal<MethodPerformace> performaceRecord = new ThreadLocal<MethodPerformace>(); public static void begin(String method) { System.out.println("begin monitor..."); MethodPerformace mp = performaceRecord.get();//獲取performanceRecord中當前執行緒共享變數的值。 if(mp == null){ mp = new MethodPerformace(method); performaceRecord.set(mp); }else{ mp.reset(method); } } public static void end() { System.out.println("end monitor..."); MethodPerformace mp = performaceRecord.get(); mp.printPerformace(); } }
(4)建立PerformanceHandler類用於安置效能監視橫切程式碼
package com.changmin.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class PerformaceHandler implements InvocationHandler { //實現InvocationHandler介面
private Object target; //宣告Object類物件target
public PerformaceHandler(Object target){
this.target = target;
} //設定PerformanceHandler類的target屬性
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
PerformanceMonitor.begin(target.getClass().getName()+"."+ method.getName());
Object obj = method.invoke(target, args); //輸出引數為args的target()方法的返回值,賦予obj
PerformanceMonitor.end();
return obj;
}
}
(5)建立代理例項並測試
package com.changmin.proxy;
import org.testng.annotations.Test;
public class ForumServiceTest {
@Test
public void proxy() {
// 使用JDK動態代理
ForumService target = new ForumServiceImpl();
PerformaceHandler handler = new PerformaceHandler(target);
ForumService proxy = (ForumService) Proxy.newProxyInstance(target
.getClass().getClassLoader(),
target.getClass().getInterfaces(), handler);
proxy.removeForum(10);
proxy.removeTopic(1012);
}
}
(6)執行結果
begin monitor...
模擬刪除Forum記錄:10
end monitor...
com.changmin.proxy.ForumServiceImpl.removeForum花費46毫秒。
begin monitor...
模擬刪除Topic記錄:1012
end monitor...
com.changmin.proxy.ForumServiceImpl.removeTopic花費32毫秒。