本節回顧一下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;
}
}