1. 程式人生 > >Spring原始碼系列(四)--spring-aop是如何設計的

Spring原始碼系列(四)--spring-aop是如何設計的

# 簡介 spring-aop 用於生成動態代理類(底層是使用 JDK 動態代理或 cglib 來生成代理類),搭配 spring-bean 一起使用,可以使 AOP 更加解耦、方便。在實際專案中,spring-aop 被廣泛用來實現日誌、許可權、事務、異常等的統一管理。 上一篇部落格([Spring原始碼系列(三)--spring-aop的基礎元件、架構和使用]( https://www.cnblogs.com/ZhangZiSheng001/p/13671149.html ))簡單講了 spring-aop 的基礎元件、架構和使用方法,本文將開始研究 spring-aop 的原始碼,主要分成以下部分: 1. spring-aop 的幾個重要的元件,如 Joinpoint、Advice、Pointcut、Advisor 等; 2. 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元件的原始碼分析](https://www.cnblogs.com/ZhangZiSheng001/p/13196228.html)。 最後,和以往不同, # 幾個重要的元件 說到 spring-aop,我們經常會提到**`Pointcut`、`Joinpoint`、`Advice`、`Aspect`**等等概念,它們都是抽象出來的“標準”,有的來自 aopalliance,有的來自 AspectJ,也有的是 spring-aop 原創。 它們是構成 spring-aop “設計圖”的基礎,理解它們非常難,一個原因是網上能講清楚的不多,第二個原因是這些元件本身抽象得不夠直觀(spring 官網承認了這一點)。 ## 對Joinpoint做Advice 在 spring-aop 的包中內嵌了 aopalliance 的包(aopalliance 就是一個制定 AOP 標準的聯盟、組織),這個包是 AOP 聯盟提供的一套“標準”,提供了 AOP 一些通用的元件,包的結構大致如下。 ```powershell └─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 沒有引入,這裡就不說了。 ![AopAopallianceUML](https://img2020.cnblogs.com/blog/1731892/202009/1731892-20200928154513960-770091995.png) 1. **Joinpoint** **`Joinpoint`表示呼叫某個方法(構造方法或成員方法),或者操作某個成員屬性的事件**。 例如,我呼叫了`user.save()` 方法,這個事件就屬於一個`Joinpoint`。`Joinpoint`是一個“動態”的概念,`Field`、`Method`、或`Constructor`等物件是它的靜態部分。 如上圖所示,**`Joinpoint`是`Advice`操作的物件**。 在 spring-aop 中,主要使用`Joinpoint`的子介面--`MethodInvocation`,JDK 動態代理使用的`MethodInvocation`實現類為`ReflectiveMethodInvocation`,cglib 使用的是`MethodInvocation`實現類為`CglibMethodInvocation`。 2. **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`等。 1. **`Advice Filter`** **`Advice Filter`一般和`Advice`繫結,它用來告訴我們,`Advice`是否作用於指定的`Joinpoint`**,如果 true,則將`Advice`加入到當前`Joinpoint`的`Advice chain`,如果為 false,則不加入。 在 spring-aop 中,常用的`Advice Filter`包括`ClassFilter`和`MethodMatcher`,前者過濾的是類,後者過濾的是方法。 2. **`Pointcut`** **`Pointcut`是 AspectJ 的元件,它一種 `Advice Filter`**。 在 spring-aop 中,`Pointcut`=`ClassFilter`+`MethodMatcher`。 3. **`Advisor`** `Advisor`是 spring-aop 原創的元件,**一個 Advisor = 一個 Advice Filter + 一個 Advice**。 在 spring-aop 中,主要有兩種`Advisor`:`IntroductionAdvisor`和`PointcutAdvisor`。前者為`ClassFilter`+`Advice`,後者為`Pointcut`+`Advice`。 4. **`Aspect`** `Aspect`也是 AspectJ 的元件,一組同類的`PointcutAdvisor`的集合就是一個`Aspect`。 在下面程式碼中,printRequest 和 printResponse 都是`Advice`,genericPointCut 是`Pointcut`,printRequest + genericPointCut 是`PointcutAdvisor`,UserServiceAspect 是`Aspect`。 ```java @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 圖來簡單說明。 ![ProxyFactoryUML](https://img2020.cnblogs.com/blog/1731892/202009/1731892-20200915090711597-1505551851.png) **`AdvisedSupport`用來告訴`AopProxy`如何生成代理物件**,它描述了兩部分資訊: 1. 對誰生成代理物件?--`TargetSource`。`TargetSource`既可以返回單例物件,也可以返回多例物件,有點類似於我們常用的`DataSource`。 2. 生成的代理物件持有的 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動態代理的使用和分析](https://www.cnblogs.com/ZhangZiSheng001/p/11917086.html) ![ProxyInvokeUML](https://img2020.cnblogs.com/blog/1731892/202009/1731892-20200928155332644-2015180344.png) 當我們呼叫代理的方法時,代理方法中將生成一個`Joinpoint`物件--即圖中的`CglibMethodInvocation`,它持有了一條`Advice chain`,而`Advice chain`通過 Advisor List 過濾得到,呼叫`Joinpoint`的`proceed()`方法就可以執行`Advice chain`。 以上簡單介紹了 spring-aop 的設計檢視,有了這些,相信讀者會更容易讀懂具體的原始碼。 感謝閱讀。以上內容如有錯誤,歡迎指正。 > 相關原始碼請移步:[spring-aop]( https://github.com/ZhangZiSheng001/spring-projects/tree/master/spring-aop ) > 本文為原創文章,轉載請附上原文出處連結:https://www.cnblogs.com/ZhangZiSheng001/p/13745168.html