Spring AOP概述

本節回顧一下AOP的概念

AOP是面向切面的編程思想,百科的概述爲:

Aspect是一種新的模塊化機制,用來描述分散在對象、類或函數中的橫切關注點。從關注點中分離出橫切關注點是面向切面的程序設計的核心概念。分離關注點是解決特定領域問題的代碼從業務邏輯中獨立出來,業務邏輯的代碼不再含有針對特定領域問題代碼的調用,業務邏輯同特定領域問題的關係通過切面來封裝、維護,這樣原本分散在整個應用程序的變動就可以很好的管理起來。


Advice通知定義在連接點做什麼,爲切面增強提供織入接口。在Spring AOP中,主要描述Spring AOP圍繞方法調用而注入的切面行爲。Advice是AOP聯盟定義的一個接口,具體的接口定義在org.aopalliance.aop.Advice中。具體的通知類型比如BeforeAdvice、AfterAdvice、AfterReturningAdvice、ThrowsAdvice等。

下面從BeforeAdvice看一下類層次關係:


可以看到MethodBeforeAdvice爲待增強的目標方法設置的前置增強接口:

public interface MethodBeforeAdvice extends BeforeAdvice {

	void before(Method method, Object[] args, @Nullable Object target) throws Throwable;

}

作爲回調函數,before方法的實現在Advice中被配置到目標方法前,會在調用目標方式時被回調。

具體的調用參數:Method對象,這個參數是目標方法的反射對象;Object[]對象數組,這個對象數組中包含目標方法的輸入參數。以CountingBeforeAdvice爲例子,CountingBeforeAdvice是MethodBeforeAdvice的具體實現:

@SuppressWarnings("serial")
public class CountingBeforeAdvice extends MethodCounter implements MethodBeforeAdvice {

	@Override
	public void before(Method m, Object[] args, Object target) throws Throwable {
		count(m);
	}

}

CountingBeforeAdvice是完成統計被調用的方法次數。作爲切面增強實現,會根據調用方法的方法名進行統計,把統計結果根據方法名和調用刺水作爲鍵值對放入一個map中。

count方法在基類MethodCounter中實現,它在對象中維護一個哈希表,用來存儲統計數據。在統計過程中,首先通過目標方法的反射對象得到方法名,然後進行累加,把統計結果放到維護的哈希表中。

public class MethodCounter implements Serializable {

	/** 這個hashMap存儲方法名和調用次數的鍵值對 */
	private HashMap<String, Integer> map = new HashMap<>();

	private int allCount;//所有的調用次數

	protected void count(Method m) {
		count(m.getName());
	}

	protected void count(String methodName) {//根據目標方法的方法名進行統計調用次數
		Integer i = map.get(methodName);
		i = (i != null) ? new Integer(i.intValue() + 1) : new Integer(1);
		map.put(methodName, i);
		++allCount;
	}

	public int getCalls(String methodName) {//根據方法名獲得調用的次數
		Integer i = map.get(methodName);
		return (i != null ? i.intValue() : 0);
	}

	public int getCalls() {//取得所有方法的調用次數
		return allCount;
	}

	/**
	 * A bit simplistic: just wants the same class.
	 * Doesn't worry about counts.
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object other) {
		return (other != null && other.getClass() == this.getClass());
	}

	@Override
	public int hashCode() {
		return getClass().hashCode();
	}

}


Pointcut切點決定Advice通知應該作用於哪個連接點,也就是說通過Pointcut定義需要增強的方法的集合,這些集合的選取可以按照一定的規則來完成。

public interface Pointcut {

	/**
	 * Return the ClassFilter for this pointcut.
	 * @return the ClassFilter (never {@code null})
	 */
	ClassFilter getClassFilter();

	/**
	 * Return the MethodMatcher for this pointcut.
	 * @return the MethodMatcher (never {@code null})
	 */
	MethodMatcher getMethodMatcher();


	/**
	 * Canonical Pointcut instance that always matches.
	 */
	Pointcut TRUE = TruePointcut.INSTANCE;

}

在接口的定義可以看到,需要返回一個MethodMatcher。對於Ponitcut的匹配判斷功能,具體由這個返回的MethodMatcher完成,也就是由MethodMatcher判斷是否需要對當前方法進行增強,或者是否需要對當前方法應用配置好的Advice通知。

切點在Spring AOP中的類繼承體系:


下面以正則表達式切點JdkRegexpMethodPointcut爲實例,瞭解一下Pointcut的工作原理。

通過基類StaticMethodMatcherPointcut可以看到,getMethodMatcher返回的是StaticMethodMatcher。

在JdkRegexpMethodPointcut可以看到一個matches方法,這個方法是使用正則表達式對方法名進行匹配的地方。

	@Override
	protected boolean matches(String pattern, int patternIndex) {
		Matcher matcher = this.compiledPatterns[patternIndex].matcher(pattern);
		return matcher.matches();
	}
此外還有通過方法名匹配進行Advice匹配的NameMatchMethodPointcut
	@Override
	public boolean matches(Method method, @Nullable Class<?> targetClass) {
		for (String mappedName : this.mappedNames) {
			if (mappedName.equals(method.getName()) || isMatch(method.getName(), mappedName)) {
				return true;
			}
		}
		return false;
	}


Advisor通知器,完成對目標方法的切面增強設計和關注點的設計以後,需要一個對象結合起來,完成這個作用的就是Advisor通知器。

我們以DefaultPointcutAdvisor爲例子,來了解Advisor的工作原理。

在DefaultPointcutAdvisor中,有兩個屬性,分別是advice和pointcut。通過這兩個屬性,可以分別配置Advice和Pointcut:

@SuppressWarnings("serial")
public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor implements Serializable {

	private Pointcut pointcut = Pointcut.TRUE;

	public DefaultPointcutAdvisor() {
	}

	public DefaultPointcutAdvisor(Advice advice) {
		this(Pointcut.TRUE, advice);
	}

	
	public DefaultPointcutAdvisor(Pointcut pointcut, Advice advice) {
		this.pointcut = pointcut;
		setAdvice(advice);
	}


	public void setPointcut(@Nullable Pointcut pointcut) {
		this.pointcut = (pointcut != null ? pointcut : Pointcut.TRUE);
	}

	@Override
	public Pointcut getPointcut() {
		return this.pointcut;
	}

	@Override
	public String toString() {
		return getClass().getName() + ": pointcut [" + getPointcut() + "]; advice [" + getAdvice() + "]";
	}

}

在DefaultPointcutAdvisor中,Pointcut默認是Pointcut.True

Pointcut TRUE = TruePointcut.INSTANCE;

TruePointcut的INSTANCE是一個單例。可以看到採用了單例模式

class TruePointcut implements Pointcut, Serializable {

	public static final TruePointcut INSTANCE = new TruePointcut();

	private TruePointcut() {
	}

	@Override
	public ClassFilter getClassFilter() {
		return ClassFilter.TRUE;
	}

	@Override//對任何方法的匹配都返回true
	public MethodMatcher getMethodMatcher() {
		return MethodMatcher.TRUE;
	}

	private Object readResolve() {
		return INSTANCE;
	}

	@Override
	public String toString() {
		return "Pointcut.TRUE";
	}

}
class TrueMethodMatcher implements MethodMatcher, Serializable {

	public static final TrueMethodMatcher INSTANCE = new TrueMethodMatcher();

	private TrueMethodMatcher() {
	}


	@Override
	public boolean isRuntime() {
		return false;
	}

	@Override
	public boolean matches(Method method, @Nullable Class<?> targetClass) {
		return true;
	}

	@Override
	public boolean matches(Method method, @Nullable Class<?> targetClass, Object... args) {
		// Should never be invoked as isRuntime returns false.
		throw new UnsupportedOperationException();
	}


	@Override
	public String toString() {
		return "MethodMatcher.TRUE";
	}

	/**
	 * Required to support serialization. Replaces with canonical
	 * instance on deserialization, protecting Singleton pattern.
	 * Alternative to overriding {@code equals()}.
	 */
	private Object readResolve() {
		return INSTANCE;
	}

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