Spring源碼系列(四)–spring-aop是如何設計的
簡介
spring-aop 用於生成動態代理類(底層是使用 JDK 動態代理或 cglib 來生成代理類),搭配 spring-bean 一起使用,可以使 AOP 更加解耦、方便。在實際項目中,spring-aop 被廣泛用來實現日誌、權限、事務、異常等的統一管理。
上一篇博客(Spring源碼系列(三)–spring-aop的基礎組件、架構和使用)簡單講了 spring-aop 的基礎組件、架構和使用方法,本文將開始研究 spring-aop 的源碼,主要分成以下部分:
- spring-aop 的幾個重要的組件,如 Joinpoint、Advice、Pointcut、Advisor 等;
- spring-aop 是如何設計的
項目環境
maven:3.6.3
操作系統:win10
JDK:8u231
Spring:5.2.6.RELEASE
一點補充
在第一篇博客中,我們使用 spring-aop 提供的代理工廠來生成動態代理類,被代理的對象可以是我們自己 new 的一個對象,也可以是 bean。因為 spring-aop 最主要的功能就是生成動態代理類,所以,本文的源碼分析都只圍繞這個功能展開,並不會摻雜 spring-bean 的內容。
實際項目中,Spring 可以「悄無聲息」地完成對 bean 的代理,本質是通過註冊BeanPostProcessor
來實現,原理並不複雜。如果你對 spring-bean 感興趣的話,可以參考博客Spring源碼系列(二)–bean組件的源碼分析。
最後,和以往不同,
幾個重要的組件
說到 spring-aop,我們經常會提到Pointcut
、Joinpoint
、Advice
、Aspect
等等概念,它們都是抽象出來的「標準」,有的來自 aopalliance,有的來自 AspectJ,也有的是 spring-aop 原創。
它們是構成 spring-aop 「設計圖」的基礎,理解它們非常難,一個原因是網上能講清楚的不多,第二個原因是這些組件本身抽象得不夠直觀(spring 官網承認了這一點)。
對Joinpoint做Advice
在 spring-aop 的包中內嵌了 aopalliance 的包(aopalliance 就是一個制定 AOP 標準的聯盟、組織),這個包是 AOP 聯盟提供的一套「標準」,提供了 AOP 一些通用的組件,包的結構大致如下。
└─org
└─aopalliance
├─aop
│ Advice.class
│ AspectException.class
│
└─intercept
ConstructorInterceptor.class
ConstructorInvocation.class
Interceptor.class
Invocation.class
Joinpoint.class
MethodInterceptor.class
MethodInvocation.class
使用 UML 表示以上類的關係,如下。可以看到,這主要包含兩個部分:Joinpoint
和Advice
(這是 AOP 最核心的兩個概念)。完整的 aopalliance 包,除了 aop 和 intercept,還包括了 instrument 和 reflect,後面這兩個部分 spring-aop 沒有引入,這裡就不說了。
- Joinpoint
Joinpoint
表示調用某個方法(構造方法或成員方法),或者操作某個成員屬性的事件。
例如,我調用了user.save()
方法,這個事件就屬於一個Joinpoint
。Joinpoint
是一個「動態」的概念,Field
、Method
、或Constructor
等對象是它的靜態部分。
如上圖所示,Joinpoint
是Advice
操作的對象。
在 spring-aop 中,主要使用Joinpoint
的子接口–MethodInvocation
,JDK 動態代理使用的MethodInvocation
實現類為ReflectiveMethodInvocation
,cglib 使用的是MethodInvocation
實現類為CglibMethodInvocation
。
- Advice
對Joinpoint
執行的某些操作。
例如,JDK 動態代理使用的InvocationHandler
、cglib 使用的MethodInterceptor
,在抽象概念上可以算是Advice
(即使它們沒有繼承Advice
)。
在 spring-aop 中,主要使用Advice
的子接口–MethodInterceptor
。
為了更好地理解這兩個概念,我再舉一個例子:當我們對用戶進行增刪改查前,進行權限校驗。其中,調用用戶的新增方法的事件就是一個的Joinpoint
,權限校驗就是一個Advice
,即對Joinpoint
做Advice
。
在 spring-aop 中,Joinpoint
對象持有了一條Advice chain
,調用Joinpoint
的proceed()
方法將採用責任鏈的形式依次執行(注意,Advice
的執行可以互相嵌套,不是單純的先後順序)。
其他的幾個概念
在 spring-aop 中,還會使用到其他的概念,例如Advice Filter
、Advisor
、Pointcut
、Aspect
等。
Advice Filter
Advice Filter
一般和Advice
綁定,它用來告訴我們,Advice
是否作用於指定的Joinpoint
,如果 true,則將Advice
加入到當前Joinpoint
的Advice chain
,如果為 false,則不加入。
在 spring-aop 中,常用的Advice Filter
包括ClassFilter
和MethodMatcher
,前者過濾的是類,後者過濾的是方法。
Pointcut
Pointcut
是 AspectJ 的組件,它一種 Advice Filter
。
在 spring-aop 中,Pointcut
=ClassFilter
+MethodMatcher
。
Advisor
Advisor
是 spring-aop 原創的組件,一個 Advisor = 一個 Advice Filter + 一個 Advice。
在 spring-aop 中,主要有兩種Advisor
:IntroductionAdvisor
和PointcutAdvisor
。前者為ClassFilter
+Advice
,後者為Pointcut
+Advice
。
Aspect
Aspect
也是 AspectJ 的組件,一組同類的PointcutAdvisor
的集合就是一個Aspect
。
在下面代碼中,printRequest 和 printResponse 都是Advice
,genericPointCut 是Pointcut
,printRequest + genericPointCut 是PointcutAdvisor
,UserServiceAspect 是Aspect
。
@Aspect
public class UserServiceAspect {
private static final Logger LOGGER = LoggerFactory.getLogger(UserServiceAspect.class);
@Pointcut("execution(* cn.zzs.spring.UserService+.*(..)))")
public void genericPointCut() {
}
@Before(value = "genericPointCut()")
public void printRequest(JoinPoint joinPoint) throws InterruptedException {
//······
}
@After(value = "genericPointCut()")
public void printResponse(JoinPoint joinPoint) throws InterruptedException {
//······;
}
}
spring-aop是如何設計的
了解了 spring-aop 的重要組件,接下來就可以構建它的設計視圖。spring-aop 的設計視圖主要包括兩個部分:生成代理類和代理方法的執行。
生成代理類
這裡我畫了一張 UML 圖來簡單說明。
AdvisedSupport
用來告訴AopProxy
如何生成代理對象,它描述了兩部分信息:
- 對誰生成代理對象?–
TargetSource
。TargetSource
既可以返回單例對象,也可以返回多例對象,有點類似於我們常用的DataSource
。 - 生成的代理對象持有的 Advisor List。前面提到過,當我們執行代理方法時,將會採用責任鏈的方式執行
Advice chain
,而Advice chain
就是通過 Advisor List 過濾得到;
AopProxy
用來生成代理對象,spring-aop 提供了 JDK 動態代理和 cglib 動態代理兩種AopProxy
實現。
除此之外,spring-aop 提供了三種代理工廠供調用者使用,其中ProxyFactory
比較普通,AspectJProxyFactory
支持 AspectJ 語法的代理工廠,ProxyFactoryBean
可以給 Spring IoC 管理的 bean 進行代理。上一篇博客已介紹過如何使用這三個代理工廠。
代理方法的執行
這裡使用 cglib 的代理類來簡單說明代理方法的執行過程。關於 cglib 的內容可以參考: 源碼詳解系列(一)——cglib動態代理的使用和分析
當我們調用代理的方法時,代理方法中將生成一個Joinpoint
對象–即圖中的CglibMethodInvocation
,它持有了一條Advice chain
,而Advice chain
通過 Advisor List 過濾得到,調用Joinpoint
的proceed()
方法就可以執行Advice chain
。
以上簡單介紹了 spring-aop 的設計視圖,有了這些,相信讀者會更容易讀懂具體的源碼。
感謝閱讀。以上內容如有錯誤,歡迎指正。
相關源碼請移步:spring-aop
本文為原創文章,轉載請附上原文出處鏈接://www.cnblogs.com/ZhangZiSheng001/p/13745168.html