死磕Spring之AOP篇 – Spring AOP總覽
- 2021 年 4 月 16 日
- 筆記
- Spring AOP, 死磕Spring之AOP篇, 源碼解析
該系列文章是本人在學習 Spring 的過程中總結下來的,裡面涉及到相關源碼,可能對讀者不太友好,請結合我的源碼注釋 Spring 源碼分析 GitHub 地址 進行閱讀。
Spring 版本:5.1.14.RELEASE
在開始閱讀 Spring AOP 源碼之前,需要對 Spring IoC 有一定的了解,可查看我的 《死磕Spring之IoC篇 – 文章導讀》 這一系列文章
了解 AOP 相關術語,可先查看 《Spring AOP 常見面試題) 》 這篇文章
該系列其他文章請查看:《死磕 Spring 之 AOP 篇 – 文章導讀》
通過上一篇 《初識 JDK、CGLIB 兩種動態代理》 文章我們對 Spring AOP 底層的 JDK 動態代理和 CGLIB 動態代理有了一定的了解,也知道如何簡單地使用兩種動態代理創建代理對象。相信上篇文章可以讓你對 Spring AOP 有了一個初步的認識,那麼接下來我們準備進入 Spring AOP 源碼學習階段。
在開始 Spring AOP 源碼學習前,本文會對 Spring AOP 涉及到的大部分主要的 API 進行介紹,讓我們對 Spring AOP 有一個全面的認識,這樣在學習 Spring AOP 源碼的過程中有一個清晰的思路會更加易於理解,然後在後續的文章從 Spring AOP 的自動代理開始深入學習,直接進入正題吧。
Spring AOP API 總覽
如下圖所示:
上面這張圖片列出了 Spring AOP 涉及到的大部分 API,接下來我們依次簡單介紹一下:
-
Joinpoint:連接點,也就是我們需要執行的目標方法
-
Pointcut:切點,提供 ClassFilter 類過濾器和 MethodMatcher 方法匹配器支援對類和方法進行篩選。
-
Advice:通知器,一般都是 MethodInterceptor 方法攔截器,不是的話會通過 AdvisorAdapter 適配器轉換成對應的 MethodInterceptor 方法攔截器。
-
Advisor:Advice 容器介面,與 Advice 是一對一的關係;它的子介面 PointcutAdvisor 相當於一種 Pointcut 和 Advice 的容器,將 Pointcut 過濾 Joinpoint 的能力和 Advice 進行整合,這樣一來就將兩者關聯起來了。
-
Advisor 適配器:AdvisorAdapter 是 Advisor 的適配器,當篩選出能夠應用於方法的所有 Advisor 後,需要獲取對應的 Advice;
- 如果不是 MethodInterceptor 類型,則需要通過 AdvisorAdapter 適配器轉換成對應的 MethodInterceptor 方法攔截器。
-
AOP 代理對象:AOP 代理對象,由 JDK 動態代理或者 CGLIB 動態代理創建的代理對象;
- 選擇哪種動態代理是通過 AopProxyFactory 代理工廠根據目標類來決定的。
-
AOP 代理配置:AdvisedSupport 配置管理器中保存了對應代理對象的配置資訊,例如滿足條件的 Advisor 對象、TargetSource 目標類來源;
- 在創建代理對象的過程,AdvisedSupport 扮演著一個非常重要的角色。
-
AOP 代理對象創建:AOP 代理對象的創建分為手動模式和自動模式;不管哪種模式都和 AdvisedSupport 配置管理器有關;
- 手動模式就是通過 Spring AOP 提供的 API 進行創建;
- 自動模式則是和 Spring IoC 進行整合,在 Bean 的載入過程中如果需要進行代理,則創建對應的代理對象;
-
AdvisorChainFactory:Advisor 鏈路工廠
- AdvisedSupport 配置管理器中保存了代理對象的所有 Advisor 對象,當攔截某個方法時,需要通過 AdvisorChainFactory 篩選出能夠應用於該方法的 Advisor 們;
- 另外還需要藉助 AdvisorAdapter 適配器獲取 Advisor 對應的 MethodInterceptor 方法攔截器,將這些 MethodInterceptor 有序地形成一條鏈路並返回。
-
IntroductionInfo:引介介面,支援代理對象實現目標類沒有真正實現的額外的介面;
- 在 Advisor 的子介面 IntroductionAdvisor 中會繼承這個 IntroductionInfo 介面,通過 @DeclareParents 註解定義的欄位會被解析成 IntroductionAdvisor 對象。
-
AOP 代理目標對象來源:目標類來源,和代理對象進行關聯,用於獲取被代理代理的目標對象;
- 在代理對象中最終的方法執行都需要先通過 TargetSource 獲取對應的目標對象,然後執行目標方法。
Joinpoint
類圖
-
ConstructorInvocation,是基於構造器的一個調用器,在 Spring AOP 中僅支援方法級別的代理,所以這個介面並沒有被使用
-
ReflectiveMethodInvocation,Joinpoint 的底層實現類,一個基於反射的方法調用器,本文不進行分析,在後續的文章進行講解😈
-
CglibMethodInvocation,和上者實現類差不多,新增了一個 CGLIB 中的 MethodProxy 方法代理對象,本文不進行分析,在後續的文章進行講解😈
org.aopalliance.intercept.Joinpoint
,連接點,也就是我們需要執行的目標方法,是由 AOP 聯盟提供的一個介面,如下:
public interface Joinpoint {
/**
* 執行方法調用器中的下一個攔截器,執行完所有的攔截器則執行目標方法
*/
Object proceed() throws Throwable;
/**
* 返回目標對象
*/
Object getThis();
/**
* 返回目標方法
*/
AccessibleObject getStaticPart();
}
org.aopalliance.intercept.Invocation
,調用器,是由 AOP 聯盟提供的一個介面,如下:
public interface Invocation extends Joinpoint {
/**
* 獲取參數數組
*/
Object[] getArguments();
}
MethodInvocation
org.aopalliance.intercept.MethodInvocation
,方法調用器,是由 AOP 聯盟提供的一個介面,如下:
public interface MethodInvocation extends Invocation {
/**
* 獲取目標方法
*/
Method getMethod();
}
ProxyMethodInvocation
org.springframework.aop.ProxyMethodInvocation
,MethodInvocation 方法調用器的擴展,允許訪問代理對象,如下:
public interface ProxyMethodInvocation extends MethodInvocation {
/**
* 返回代理對象
*/
Object getProxy();
/**
* 克隆方法
*/
MethodInvocation invocableClone();
/**
* 克隆方法,使用參數
*/
MethodInvocation invocableClone(Object... arguments);
/**
* 設置參數數組
*/
void setArguments(Object... arguments);
/**
* 添加自定義屬性
*/
void setUserAttribute(String key, @Nullable Object value);
/**
* 獲取自定義屬性
*/
@Nullable
Object getUserAttribute(String key);
}
Pointcut
類圖
- AspectJExpressionPointcut,基於 AspectJ 處理表達式的能力實現的一個 Pointcut,本文不進行分析,在後續的文章進行講解😈
上述類圖僅列出了部分 Pointcut 相關的介面
org.springframework.aop.Pointcut
,切點,提供 ClassFilter 類過濾器和 MethodMatcher 方法匹配器支援對類和方法進行篩選,如下:
public interface Pointcut {
/**
* 返回類過濾器
*/
ClassFilter getClassFilter();
/**
* 返回方法匹配器
*/
MethodMatcher getMethodMatcher();
/**
* 總是匹配通過的 Pointcut 實例對象
*/
Pointcut TRUE = TruePointcut.INSTANCE;
}
org.springframework.aop.support.ExpressionPointcut
,支援表達式匹配的 Pointcut,如下:
public interface ExpressionPointcut extends Pointcut {
/**
* 返回表達式
*/
@Nullable
String getExpression();
}
StaticMethodMatcherPointcut
org.springframework.aop.support.StaticMethodMatcherPointcut
,抽象類,本身是一個方法匹配器的 Pointcut,如下:
public abstract class StaticMethodMatcherPointcut extends StaticMethodMatcher implements Pointcut {
/** 默認情況類過濾器總是匹配通過 */
private ClassFilter classFilter = ClassFilter.TRUE;
/**
* 設置類過濾器
*/
public void setClassFilter(ClassFilter classFilter) {
this.classFilter = classFilter;
}
/**
* 獲取類過濾器
*/
@Override
public ClassFilter getClassFilter() {
return this.classFilter;
}
/**
* 返回方法匹配器,就是當前對象,因為當前對象繼承的 StaticMethodMatcher 就是一個 MethodMatcher
*/
@Override
public final MethodMatcher getMethodMatcher() {
return this;
}
}
我們自定義的 Pointcut 通常可以繼承 StaticMethodMatcherPointcut 來實現,重寫 MethodMatcher matches(Method method, Class<?> targetClass, Object... args)
方法即可,也可以再實現 ClassFilter,實現 matches(Class clazz)
方法
ClassFilter
org.springframework.aop.ClassFilter
,函數式介面,類過濾器,用於判斷這個 Class 是否滿足 Pointcut 切點,如下:
@FunctionalInterface
public interface ClassFilter {
/**
* 匹配這個 Class 類
*/
boolean matches(Class<?> clazz);
/**
* 總是匹配通過的類過濾器
*/
ClassFilter TRUE = TrueClassFilter.INSTANCE;
}
MethodMatcher
org.springframework.aop.MethodMatcher
,方法匹配器,用於判斷這個 Method 是否滿足 Pointcut 切點,如下:
public interface MethodMatcher {
/**
* 匹配這個方法
*/
boolean matches(Method method, Class<?> targetClass);
/**
* 當前方法匹配器是否為運行狀態
*/
boolean isRuntime();
/**
* 匹配這個方法,並校驗參數
*/
boolean matches(Method method, Class<?> targetClass, Object... args);
/**
* 總是匹配通過的方法匹配器
*/
MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
}
Advice
類圖
- Advice,標記介面
- BeforeAdvice,標記介面,後置通知
- AfterAdvice,標記介面,前置通知
- Interceptor,標記介面,攔截器
- AbstractAspectJAdvice,包裝了一個 Aspect 切面或者 AspectJ 註解標註的方法,Around、Before、After、AfterReturning、AfterThrowing 幾種類型的 Advice 最終都會轉換成 AbstractAspectJAdvice 對應的子類並實現 MethodInterceptor 方法攔截器介面,本文不進行分析,在後續的文章進行講解😈
上述類圖僅列出了部分 Advice 相關的介面
MethodInterceptor
org.aopalliance.intercept.MethodInterceptor
,方法攔截器,函數式介面,如下:
@FunctionalInterface
public interface MethodInterceptor extends Interceptor {
/**
* 執行 MethodInvocation 調用器
*/
Object invoke(MethodInvocation invocation) throws Throwable;
}
Advisor
類圖
上面只列出了 Advisor 的部分實現類,在 Spring AOP 中還有非常多的實現類,在後續的文章進行分析😈
- AbstractBeanFactoryPointcutAdvisor,關聯 BeanFactory 的 PointcutAdvisor 抽象類,支援從 IoC 容器中獲取 Advice 對象,我們可以通過繼承這個類來自定義一個 PointcutAdvisor 對象
org.springframework.aop.Advisor
,Advice 容器介面,與 Advice 是一對一的關係,如下:
public interface Advisor {
/**
* 空的 Advice
*/
Advice EMPTY_ADVICE = new Advice() {};
/**
* 獲取 Advice
*/
Advice getAdvice();
/**
* 返回這個 Advice 是否有一個指定的目標實例對象,默認都是 true
*/
boolean isPerInstance();
}
PointcutAdvisor
org.springframework.aop.PointcutAdvisor
,繼承 Advisor 介面,Pointcut 和 Advice 的容器,將 Pointcut 過濾 Joinpoint 的能力和 Advice 進行整合,這樣一來就將兩者關聯起來了,如下:
public interface PointcutAdvisor extends Advisor {
/**
* 獲取 Pointcut 切點
*/
Pointcut getPointcut();
}
IntroductionAdvisor
org.springframework.aop.IntroductionAdvisor
,繼承 Advisor 和 IntroductionInfo 介面,支援代理對象實現目標類沒有真正實現的額外的介面;通過 @DeclareParents 註解定義的欄位會被解析成 IntroductionAdvisor 對象,如下:
public interface IntroductionAdvisor extends Advisor, IntroductionInfo {
/**
* 獲取類載入器,因為沒有 Pointcut 切點,則提供一個類過濾器
*/
ClassFilter getClassFilter();
/**
* 校驗是否實現 IntroductionInfo 中的介面
*/
void validateInterfaces() throws IllegalArgumentException;
}
public interface IntroductionInfo {
/**
* 返回額外的介面
*/
Class<?>[] getInterfaces();
}
AdvisorAdapter
類圖
org.springframework.aop.framework.adapter.AdvisorAdapter
,Advisor 的適配器,當篩選出能夠應用於方法的所有 Advisor 後,需要獲取對應的 Advice;如果不是 MethodInterceptor 類型,則需要通過 AdvisorAdapter 適配器轉換成對應的 MethodInterceptor 方法攔截器,如下:
public interface AdvisorAdapter {
/**
* 是否支援該類型的 Advice 適配
*/
boolean supportsAdvice(Advice advice);
/**
* 獲取 Advisor 的 Advice,並包裝成對應的 MethodInterceptor 方法攔截器
*/
MethodInterceptor getInterceptor(Advisor advisor);
}
org.springframework.aop.framework.adapter.MethodBeforeAdviceAdapter
,前置 Advice 的適配器,如下:
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof MethodBeforeAdvice);
}
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}
}
可以看到這個 MethodBeforeAdviceAdapter 適配器只能在 Spring 內部使用,先判斷 Advice 是否為 MethodBeforeAdvice 類型,如果是的話,則可以將 Advice 包裝成 MethodBeforeAdviceInterceptor 對象,其他的適配器類似。
當然,AdvisorAdapter 適配器不是直接被使用,而是通過 DefaultAdvisorAdapterRegistry 適配器註冊中心使用的,在後續文章進行分析😈
AOP 代理對象
類圖
org.springframework.aop.framework.AopProxy
,AOP 代理介面,用於創建代理對象,如下:
public interface AopProxy {
/**
* 創建一個代理對象
*/
Object getProxy();
/**
* 創建一個代理對象,傳入指定的 ClassLoader 類載入器
*/
Object getProxy(@Nullable ClassLoader classLoader);
}
在 Spring AOP 中有 JdkDynamicAopProxy 和 CglibAopProxy 兩種實現類,分別代表 JDK 動態代理和 CGLIB 動態代理。選擇哪種動態代理是通過 DefaultAopProxyFactory 代理工廠根據目標類來決定的,這些內容在後續文章進行分析😈
AOP 代理配置
類圖
org.springframework.aop.framework.ProxyConfig
,代理配置類,如下:
/**
* @see AdvisedSupport
*/
public class ProxyConfig implements Serializable {
/** use serialVersionUID from Spring 1.2 for interoperability. */
private static final long serialVersionUID = -8409359707199703185L;
/** 是否使用類代理 */
private boolean proxyTargetClass = false;
private boolean optimize = false;
boolean opaque = false;
/** 是否暴露代理對象 */
boolean exposeProxy = false;
/** 配置是否被凍結 */
private boolean frozen = false;
}
這個類僅提供一些頂層的配置資訊,更多和創建代理對象相關的配置資訊都在子類 AdvisedSupport 中,這些內容在後續文章進行分析😈
AOP 代理對象創建
類圖
AOP 代理對象的創建分為手動模式和自動模式;不管哪種模式都和 AdvisedSupport 配置管理器有關;
- 手動模式就是通過 Spring AOP 提供的 API 進行創建,例如 ProxyFactory 代理工廠;
- 自動模式則是和 Spring IoC 進行整合,在 Bean 的載入過程中如果需要進行代理,則創建對應的代理對象;可以看到 AbstractAutoProxyCreator 實現了 BeanPostProcessor 相關介面,在載入 Bean 的過程中,初始化後會調用 BeanPostProcessor#postProcessAfterInitialization 的初始化後置處理方法,在 AbstractAutoProxyCreator 中則可以創建對應的代理對象(如果有必要的話)
上面這些 AOP 自動代理類在後續文章會進行分析😈
AOP 代理目標對象來源
類圖
org.springframework.aop.TargetSource
,目標類來源,和代理對象進行關聯,用於獲取被代理代理的目標對象,如下:
public interface TargetSource extends TargetClassAware {
/**
* 獲取目標對象的 Class 對象
*/
@Override
@Nullable
Class<?> getTargetClass();
/**
* 是否是靜態的,返回 `fasle` 表示每次獲取目標對象都需要進行創建,那麼每次創建都需要釋放,例如原型模式的 Bean
*/
boolean isStatic();
/**
* 獲取目標對象
*/
@Nullable
Object getTarget() throws Exception;
/**
* 釋放目標對象
*/
void releaseTarget(Object target) throws Exception;
}
SingletonTargetSource
org.springframework.aop.target.SingletonTargetSource
,單例 Bean 的目標對象來源,如下:
public class SingletonTargetSource implements TargetSource, Serializable {
/** use serialVersionUID from Spring 1.2 for interoperability. */
private static final long serialVersionUID = 9031246629662423738L;
/** 保存目標對象 */
private final Object target;
/**
* 創建實例對象的時候就是設置目標對象
*/
public SingletonTargetSource(Object target) {
Assert.notNull(target, "Target object must not be null");
this.target = target;
}
@Override
public Class<?> getTargetClass() {
return this.target.getClass();
}
@Override
public Object getTarget() {
return this.target;
}
@Override
public void releaseTarget(Object target) {
// nothing to do
}
@Override
public boolean isStatic() {
return true;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof SingletonTargetSource)) {
return false;
}
SingletonTargetSource otherTargetSource = (SingletonTargetSource) other;
return this.target.equals(otherTargetSource.target);
}
@Override
public int hashCode() {
return this.target.hashCode();
}
@Override
public String toString() {
return "SingletonTargetSource for target object [" + ObjectUtils.identityToString(this.target) + "]";
}
}
單例 Bean 對應的 TargetSource 都是 SingletonTargetSource,裡面保存了這個單例 Bean。
總結
本文開頭的圖片展示了 Spring AOP 涉及到的大部分主要的 API,圍繞著這張圖片對這些 API 進行簡單的講述,對 Spring AOP 可以有一個整體的認識。不過 Spring AOP 還有一個部分,如何開啟整個 AOP 模組在本文沒有體現出來,簡單提一下,通過 @EnableAspectJAutoProxy
註解或者 <aop:aspectj-autoproxy />
XML 配置可以激活 AOP 模組,底層會註冊一個 AbstractAutoProxyCreator 類型的 Bean 完成 AOP 自動代理,具體過程在後面的文章進行分析😈
-
在 Spring AOP 中有著
Joinpoint
連接點,Pointcut
切點和Advice
通知,通過Advisor
介面保存一個Advice
,其子介面PointcutAdvisor
又關聯一個Pointcut
,將Pointcut
過濾Joinpoint
的能力和Advice
進行整合,這樣一來就將兩者關聯起來了。 -
Spring AOP 傳遞
Advice
的過程通常是通過Advisor
實現的,而使用Advice
的過程都是通過MethodInterceptor
執行,如果不是該類型則通過AdvisorAdapter
適配器將其包裝成對應的MethodInterceptor
方法攔截器。 -
Sping AOP 底層有 JDK 動態代理和 CGLIB 動態代理兩種方式,分別對應
JdkDynamicAopProxy
和CglibAopProxy
,選擇哪種代理方式需要通過DefaultAopProxyFactory
代理工廠根據目標類來決定。 -
在 Spring AOP 中一個
AdvisedSupport
配置管理器,裡面保存對應代理對象的配置資訊,例如滿足條件的Advisor
對象、TargetSource
目標類來源,AdvisedSupport
在 Spring AOP 中扮演一個重要的角色。 -
Spring AOP 中的自動代理主要由
AbstractAutoProxyCreator
這個 Bean 進行創建,因為它實現了幾種BeanPostProcessor
,例如在 Bean 載入過程中,初始化後會調用AbstractAutoProxyCreator
的方法進行處理,返回一個代理對象(如果有必要的話)。
在後續的文章我們主要圍繞自動代理進行分析,對於 Bean 的載入過程不熟悉的小夥伴可查看我前面的《死磕Spring之IoC篇 – 文章導讀》文章,或者直接查看《死磕Spring之IoC篇 – Bean 的創建過程》 這篇文章