Spring AOP 剖析(4)

Spring AOP 的實現機制

 

 

Spring AOP 的設計哲學也是簡單而強大的。 它不打算將所有的 AOP 需求全部囊括在內,而是要以有限的 20% 的 AOP

 

支持,在滿足 80% 的 AOP 需求。 如果覺得 Spring AOP 無法滿足你所需要的那 80% 之外的需求,那麼可以求助於

 

AspectJ 來完成, Spring AOP 對 AspectJ 提供了很好的集成。

 

 

Spring AOP 屬於第二代 AOP, 採用動態代理機制和字節碼生成技術實現 。與最初的 AspectJ 採用編譯器將橫切邏輯織入

 

目標對象不同,動態代理機制和字節碼生成都是在運行期間爲目標對象生成一個代理對象,而將橫切邏輯織入到這個代理對象


中,系統最終使用的是織入了橫切邏輯的代理對象,而不是真正的目標對象。

 


1.  動態代理機制

 

關於代理模式:

 


在這個場景中,Client 想要請求具體的 SubjectImpl 實例,但是 Client 無法直接請求真正要訪問的資源 SubjectImpl

 

而是必須通過 ISubject 資源的訪問代理類 SubjectProxy 進行。

 

package prx.aop.proxy;

public interface ISubject {

	public void request();
}
 
package prx.aop.proxy;

public class SubjectImpl implements ISubject {

	public void request() {
		System.out.println("this is the subjectImpl request logic");
	}

}
 
package prx.aop.proxy;

public class SubjectProxy implements ISubject {
	private ISubject subject;
	
	public SubjectProxy() {
		this.subject = new SubjectImpl();
	}
	
	public void request() {
		subject.request();
	}

}
 
package prx.aop.proxy;

public class Client {

	public static void main(String[] args) {
		ISubject proxyObject = new SubjectProxy();
		
		//Client 想訪問 SubjectImpl,但是找它不到,就去找 它的代理 SubjectProxy, 要 這個代理去通知 SubjectImpl
		proxyObject.request();
	}
}

 

當 Client 通過 request() 請求服務的時候, SubjectProxy 將請求轉發給了 SubjectImpl 。 從這個角度上說,

 

SubjectProxy 反而有多此一舉之嫌了。 不過, SubjectProxy 的作用不只侷限於請求的轉發, 更多時候是對請求添加

 

更多訪問限制。

 

 

假設現在要增加系統需求,對 SubjectImpl 增加一個限制條件,只有在 上午 9 點 到 下午 6 點之間才能求情數據。

 

這是我們只需要修改 代理對象 就可以了。

 

package prx.aop.proxy;

import org.joda.time.TimeOfDay;

public class SubjectProxy implements ISubject {
	private ISubject subject;
	
	public SubjectProxy() {
		this.subject = new SubjectImpl();
	}
	
	public void request() {
		TimeOfDay startTime = new TimeOfDay(9, 0, 0);
		TimeOfDay endTime = new TimeOfDay(17, 59, 59);
		TimeOfDay currentTime = new TimeOfDay();
		
		if(! (currentTime.isAfter(startTime) && currentTime.isBefore(endTime)) ) {
			return;
		}
		
		subject.request();
	}

}

 

代理對象 SubjectProxy 就像是 SubjectImpl 的影子, 只不過這個影子通常擁有更過的功能。 如果 SubjectImpl 是

 

系統中的 Joinpoint 所在的對象, 即目標對象,那麼就可以爲這個目標對象創建一個代理對象,然後將橫切邏輯添加到

 

這個代理對象中。 當系統使用這個代理對象運行的時候,原有邏輯的實現和橫切邏輯就完全融合到一個系統中了。

 


Spring AOP 本質上就是採用這種代理機制實現的 , 但是,還有點差別。

 

 

這是因爲, 系統中可不一定就 ISubject 的實現類有 request() 方法, IRequestable 接口以及相應實現類可能也有

 

request() 方法, 它們也是我們需要橫切的關注點。

 

package prx.aop.proxy;

public interface IRequestable {

	public void request();
}
 
package prx.aop.proxy;

public class RequestableImpl implements IRequestable {

	public void request() {
		System.out.println(" request processed in RequestableImpl ");
	}

}

 

爲了能夠爲 IRequestable 相應實現類也織入以上的橫切邏輯, 我們又得提供對應的代理對象

 

package prx.aop.proxy;

import org.joda.time.TimeOfDay;

public class RequestableProxy implements IRequestable {
	private IRequestable targetObject;
	
	public RequestableProxy(IRequestable targetObject) {
		this.targetObject = targetObject;
	}
	
	public void request() {
		TimeOfDay startTime = new TimeOfDay(9, 0, 0);
		TimeOfDay endTime = new TimeOfDay(17, 59, 59);
		TimeOfDay currentTime = new TimeOfDay();
		
		if(! (currentTime.isAfter(startTime) && currentTime.isBefore(endTime)) ) {
			return;
		}
		
		targetObject.request();
	}

}

 

並且將該代理對象而不是目標對象綁定到系統中

 

IRequestable targetObject = new RequestableImpl();
IRequestable proxy = new RequestableProxy(targetObject);
proxy.request();

 

於是問題出現。 雖然 Joinpoint 相同 (request() 方法的執行 ), 但是對應的目標對象類型是不一樣的。 針對不一樣的

 

目標對象類型,需要爲其單獨實現一個代理對象。 而實際上,這些代理對象所有添加的橫切邏輯是一樣的。 當系統中存在

 

成百上千的符合 Pointcut 匹配條件的目標對象時,我們就要爲這成百上千的目標對象創建成百上千的代理對象。

 

 

這種爲對應的目標對象創建靜態代理的方法,原理上是可行的,但具體應用上存在問題,所以要尋找其他方法,以避免以上

 

問題。

 

 

動態代理

 

JDK 1.3 後引入了一種稱之爲動態代理 (Dynamic Proxy)的機制。使用該機制,我們可以爲指定的接口在系統運行期間

 

動態的生成代理對象, 從而幫助我們走出最初使用靜態代理實現 AOP 的窘境。

 

 

動態代理機制的實現主要由  java.lang.reflect.Proxy 類  和 java.lang.reflect.InvocationHandler 接口組成。

 

使用動態代理實現前面的 “request時間控制” 功能。

 

package prx.aop.proxy;

public interface ISubject {

	public void request();
}
 
package prx.aop.proxy;

public class SubjectImpl implements ISubject {

	public void request() {
		System.out.println("this is the subjectImpl request logic");
	}

}
 
package prx.aop.proxy;

public interface IRequestable {

	public void request();
}
 
package prx.aop.proxy;

public class RequestableImpl implements IRequestable {

	public void request() {
		System.out.println(" request processed in RequestableImpl ");
	}

}
 
package prx.aop.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

import org.joda.time.TimeOfDay;

public class RequestInvocationHandler implements InvocationHandler{
	private Object target;
	
	public RequestInvocationHandler(Object target) {
		this.target = target;
	}
	
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		if(method.getName().equals("request")) {
			TimeOfDay startTime = new TimeOfDay(9, 0, 0);
			TimeOfDay endTime = new TimeOfDay(17, 59, 59);
			TimeOfDay currentTime = new TimeOfDay();
			
			if(! (currentTime.isAfter(startTime) && currentTime.isBefore(endTime)) ) {
				System.out.println("service is not avaliable now.");
				return null;
			} else {
				System.out.println("service is avaliable now");
			}
			
			return method.invoke(target, args);
		}
		return null;
	}

}
 
package prx.aop.proxy;

import java.lang.reflect.Proxy;

public class Client {

	public static void main(String[] args) {
		ISubject subject = (ISubject) Proxy.newProxyInstance(
				Client.class.getClassLoader(), 
				new Class[]{ISubject.class}, 
				new RequestInvocationHandler(new SubjectImpl()));
		subject.request();
		
		IRequestable requestable = (IRequestable) Proxy.newProxyInstance(
				Client.class.getClassLoader(), 
				new Class[]{IRequestable.class}, 
				new RequestInvocationHandler(new RequestableImpl()));
		requestable.request();
	}
}

 

即使還有更多的目標對象,只要它依然織入的橫切邏輯相同,用 RequestInvocationHandler 一個類並通過 Proxy 爲

 

它們生成相應的動態代理實例就可以滿足要求。 當 Proxy 動態生成的代理對象上相應的接口方法被調用時,對應的

 

InvocationHandler 就會攔截相應的方法調用,並進行相應處理。

 

InvocationHandler 就是我們事先橫切邏輯的地方,它是橫切邏輯的載體,作用跟 Advice 是一樣的。

 

 

動態代理雖好,但是不能滿足所有的需求。因爲動態代理機制只能對實現了相應 Interface 的類使用,如果某個類沒有

 

實現任何的 Interface,就無法使用動態代理機制爲其生成動態代理對象。 雖然面向接口編程應該是提倡的做法,但

 

不排除其他的編程實踐。 對於沒有實現任何 Interface 的目標對象,我們需要尋找其他方式爲其動態的生成代理對象。

 


2.  動態字節碼生成

 

使用動態字節碼生成技術擴展對象行爲的原理是:對目標對象進行集成擴展,爲其生成相應的子類,而子類可以通過覆寫來


擴展父類的行爲,只要將橫切邏輯的實現放到子類中,然後讓系統使用擴展後的目標對象的子類,就可以達到與代理模式


相同的效果了。

 


使用繼承的方式來擴展對象定義,也不能像靜態代理模式那樣,爲每個不同類型的目標對象都創建相應的擴展子類。

 

所以,需要借組與 CGLIB 這樣的動態字節碼生成庫,在系統運行期間動態的爲目標對象生成相應的擴展子類。

 

package prx.aop.proxy;

public class Requestable {

	public void request() {
		System.out.println("requestable without implementint any interface");
	}
}
 
package prx.aop.proxy;

import java.lang.reflect.Method;

import org.joda.time.TimeOfDay;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class RequestCallback implements MethodInterceptor {

	public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable {
		if(method.getName().equals("request")) {
			TimeOfDay startTime = new TimeOfDay(9, 0, 0);
			TimeOfDay endTime = new TimeOfDay(17, 59, 59);
			TimeOfDay currentTime = new TimeOfDay();
			
			if(! (currentTime.isAfter(startTime) && currentTime.isBefore(endTime)) ) {
				System.out.println("service is not avaliable now.");
				return null;
			} else {
				System.out.println("service is avaliable now");
			}
			
			return proxy.invokeSuper(object, args);
		}
		return null;
	}

}
 
package prx.aop.proxy;

import net.sf.cglib.proxy.Enhancer;

public class Client {

	public static void main(String[] args) {
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(Requestable.class);
		enhancer.setCallback(new RequestCallback());
		
		Requestable proxy = (Requestable) enhancer.create();
		proxy.request();
	}
}

 

RequestCallback 實現了對 request() 方法請求進行訪問控制的橫切邏輯。 然後通過 CGLIB 的 Enhancer 爲

 

目標對象動態地成成一個子類,並將 RequestCallback 中的橫切邏輯附加到該子類中。

 

 

通過爲 enhancer 指定需要生成的子類對應的父類,以及 Callback 實現, enhancer 最終生成了需要的代理對象實例。

 

使用 CGLIB 對類進行擴展的唯一限制就是 無法對 final 方法進行覆寫。

 

 

 

以上兩種技術(即:動態代理 與 動態字節碼生成) 就是 Spring AOP 所使用的核心技術。 也就是 Spring AOP 的

 

Weaving And Weaver 的實現原理了。

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