Spring AOP 實例解析(InvocationHandler 的invoke的proxy 參數作用)

代理模式:爲其他對象提供一種代理以便控制對這個對象的訪問(所謂控制,就是可以在其調用行爲,前後分別加入一些操作)。代理模式分類:1.靜態代理(其實質是類的繼承,比較容易理解)2.動態代理。

用JDK 動態代理實現面向切面編程(還有CGLIB 也可實現動態代理,且不需要真實對象類實現接口,但真實對象不能是final 類),以下Demo 實現在特定方法前後添加日誌和事務的的功能。

1)Proxy 類 (真實對象的代理) 的三個參數

    前兩個參數用於生成$Proxy 的構造器,此構造器再以invocationHandler 爲參數生成$Proxy 實例。

  • classLoader: 類加載器,它由真實對象userService 得到,但其實跟userService 不是一一對應的關係;用任何一個對象都可以用同樣的方法得到一樣的類加載器;既然如此,用測試類本身也可以得到類加載器:this.getClass().getClassLoader(),再把this 省略,就變成了getClass().getClassLoader()
    類加載器打印出來就是:sun.misc.Launcher$AppClassLoader@18b4aac2
  • interfaces: 接口數組,真實對象userService 所實現的所有接口。Proxy 用類加載器和接口數組這兩個參數得到動態生成的Proxy 的Class 對象(getProxyClass0(loader, intfs)),再用此Class 對象獲取其構造器(cl.getConstructor(constructorParams)),由構造器生成Proxy 實例(cons.newInstance(new Object[]{h}), 此處的h 就是Proxy 第三個參數invocationHandler). 
  • invocationHandler: 直譯是調用處理器,它這個對象本質是Proxy 類的代理,也就是真實對象的代理 的代理。Proxy類實現了真實對象的接口,所以它的對象可以調用真實對象的方法,調用時是指派invocationHandler 去實際執行系統增強業務(即日誌、事務等) 和真實業務。

2)InvocationHandler 的invoke 三個參數的作用

  • proxy: 就是Proxy 的一個動態實例,用Idea debug 顯示爲:{$Proxy4@862} com.qf.service.impl.UserServiceImpl@5a8806ef. 這個參數在invoke 方法體內可以完全不用,所以很難理解它的作用,甚至知乎、Stackoverflow 上的回答都感覺不太對。Stackoverflow 上一個回答說作用是可以將代理對象返回以進行連續調用,並寫了demo(https://stackoverflow.com/questions/22930195/understanding-proxy-arguments-of-the-invoke-method-of-java-lang-reflect-invoca),難道不需要連續調用的話就沒用了嗎?又查了很多資料,終於找到了一個比較符合邏輯的解釋:invoke 第二個參數是method 怎麼來的?$Proxy 動態實例 (即第一個參數proxy) 生成後(是class 文件),通過反向編譯,可以看到裏面有如下一段靜態代碼塊(來源:http://rejoy.iteye.com/blog/1627405),也就是說只有proxy 實例在InvocationHandler 實現類里加載才能產生第二個參數method (靜態代碼塊是虛擬機加載類的時候執行的,而且只執行一次),所以$Proxy 實例要把自己傳給InvocationHandler 的invoke 方法。
m3 = Class.forName("dynamic.proxy.UserService").getMethod("add", new Class[0]);

    InvocationHandler 源碼中對proxy 的註釋:the proxy instance that the method was invoked on

  • method: 真實對象要實現的業務方法,由$Proxy 實例的靜態代碼塊得到。
  • args: 第二個參數method 的參數。

3)兩個概念:系統增強服務(日誌、事務、權限等) 與系統真實業務分離,放到 InvocationHandler 實現類invoke 方法裏面的 這些出代碼片段就是一個切面,本文Demo 中真實業務add() 就是切入點


以下爲InvocationHandler 實現類和測試類(含創建$Proxy 動態實例) 的代碼。

public class DynamicProxy implements InvocationHandler {
	private TransactionManager tx;
	private LoggerManager log;
	private Object object;

	public DynamicProxy() {
	}
        //object 參數爲真實對象,此例中爲userService 對象
	public DynamicProxy(TransactionManager tx, LoggerManager log, Object object) {
		this.tx = tx;
		this.log = log;
		this.object = object;
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		tx.begin();
		log.beginLog();
		Object invoke = method.invoke(object, args);
		log.endLog();
		tx.commit();
		return invoke;
	}
}
        @Test
	public void testDynamicAOP(){
		LoggerManager log = new LoggerManager();
		TransactionManager tm = new TransactionManager();
		IUserService userService = new UserServiceImpl();
		ClassLoader loader = userService.getClass().getClassLoader();
		Class<?>[] interfaces = userService.getClass().getInterfaces();
		InvocationHandler handler = new DynamicProxy(tm, log, userService);
		IUserService userServiceProxy = (IUserService) Proxy.newProxyInstance(loader, interfaces, handler);
		userServiceProxy.add(new User("Rock"));
	}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章