AOP,面向切面程式設計,主要的作用是可以 将那些分散在業務系統中相同的代碼抽取出來放到一個地方進行管理
這麼做的好處是減少了重複代碼的編寫,并且軟體的可維護性也強
為什麼叫做面向切面程式設計呢?
舉個例子:假如我們的代碼中,
- 有許多以update開頭的函數的執行都需要管理者權限。如果不使用AOP,那麼我們在每個以update開頭的函數中都要進行權限驗證,這樣導緻了大量重複代碼的産生
- 與此同時,萬一某天需求有變,不再限制隻有管理者才能執行這些函數,那麼我們又要将原來代碼中和這個部分相關的代碼逐行移除,十分的麻煩
引入了AOP之後,這項工作就變得簡單了
- 我們可以将權限驗證的代碼放在某個地方,然後通過某些特定的配置實作在執行系統中以update開頭的函數之前,先執行權限驗證的代碼
- 如此,萬一需求變了,我們也隻要改一個地方的代碼。那一個個以update開頭的函數就是切點,橫向地來看,可以把它們抽象成一個切面,是以AOP被稱為面向切面程式設計。
常見的應用場景:日志記錄、性能統計、安全認證、事務處理、異常處理等
我們将這些代碼從業務邏輯代碼中分離出來,通過對這些行為的分離,我們把它們獨立到非指導業務邏輯的代碼中,進而改變這些代碼的時候不會影響到我們的業務邏輯代碼
并且業務邏輯代碼也感覺不到它們的存在,因為業務邏輯代碼“被代理了”。
Spring AOP中代理機制的實作主要使用了了JDK動态代理以及CGLIB動态代理
基本的概念
-
連接配接點
目标被增強的函數即程式執行過程中的行為,比如方法調用或特定異常被抛出
-
Advice通知
定義在連接配接點做什麼,為切面增強提供織入接口,有Before/After/ThrowsAdvice
在特定的連接配接點,AOP 架構執行的動作
Spring 以攔截器作通知模型,維護一個圍繞連接配接點的攔截器鍊
-
Pointcut切點
決定Advice應該作用于哪個連接配接點,也就是說通過Pointcut來定義需要增強的方法的集合
-
Advisor通知器
将目标方法的切面增強設計(Advice)和關注點的設計(Pointcut)結合起來。通過Advisor,可以定義該使用哪個通知并在哪個關注點使用它
1 Advice
Advice是AOP中的一個基本接口,BeforeAdvice、AfterAdvice、ThrowsAdvice等都繼承于它
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIn5GcuQWZilDN2ImZ2Q2MyI2N3I2M0QTYmZjMhZ2N5ATYzYGZfdWbp9CXt92Yu4GZjlGbh5SZslmZxl3Lc9CX6MHc0RHaiojIsJye.png)
Advice繼承關系圖
在
BeforeAdvice
的繼承關系中,定義類為待增強的目标方法設定的前置增強接口
MethodBeforeAdvice
使用這個前置接口需要實作一個回調函數
before
,作為回調函數,before方法的實作在Advice中被配置到目标方法後,會在調用目标方法時被回調
MethodBeforeAdvice以及回調函數before
before的參數
- Method:目标方法的反射對象
- Object[]:包含目标方法的輸入參數
同樣的,在AfterAdvice繼承體系下的AfterReturningAdvice中也有相似的回調函數
圖1.3 AfterReturningAdvice及其回調函數afterReturn
2 Pointcut切點
從Pointcut的基本接口定義中可以看到,需要傳回一個
MethodMatcher
Point的比對判斷,即
- 判斷是否需要對目前方法調用進行增強
- 或者是否需要對目前調用方法應用配置好的Advice通知
談談Spring AOP基本的概念2 Pointcut切點 Pointcut的基本接口定義
而在
接口中,有一個MethodMatcher
matcher
方法
在比對連接配接點的過程中起着至關重要的作用.
MethodMatcher
對象是可以配置成
-
有一個MethodMatcher接口定義的JdkRegexpMethodPointcut
方法,用正規表達式來對方法名進行比對判斷matches
-
完成方法的比對判斷NameMatchMethodPointcut
PointCutadvisor 有 兩 個 常 用 實 現 類
-
NameMatchMethodPointCutadvisor
需要注入 mappedname 和 advice 屬性,mappedname 指明要攔截的方法名
-
regexMethodPointCutadvisor
需要注入 pattern 和 advice 屬性,pattern 按照正規表達式的方法指明了要攔截的方法名,
advice 定義一個增強,即要加入的操作(需要自己實作 MethodBeforeAdvice、MethodafterAdvice、throwAdvice、Methodinterceptor 接口之一),然後在 ProxyBeanFactory 的攔截器中注入這個 PointCutadvisor。注:一個 ProxyFactoryBean 隻能指定一個代理目标。
JdkRegexpMethodPointcut中的matches函數
在NameRegexpMethodPointcut中,給出了matches方法的另一個實作,根據方法的全限定名稱進行比對
NameRegexpMethodPointcut中matches函數的實作
NameMatchMethodPointcut中matches方法的調用關系鍊
JdkRegexpMethodPointcut中matches方法的調用關系鍊
從圖2.4和圖2.5中我們可以看到,在JdkDynamicAopProxy的invoke方法中發出了對matches方法的調用.這個invoke方法就是Proxy對象進行代理回調的入口方法.
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface()) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
/**
* Determine whether the supplied {@link AdvisedSupport} has only the
* {@link org.springframework.aop.SpringProxy} interface specified
* (or no proxy interfaces specified at all).
*/
private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
Class<?>[] interfaces = config.getProxiedInterfaces();
return (interfaces.length == 0 || (interfaces.length == 1 && SpringProxy.class.equals(interfaces[0])));
}
}
JdkDynamicAopProxy的類圖
// Get the interception chain for this method.
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
}
else {
// We need to create a method invocation...
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
}