1. 程式人生 > 其它 >Spring之AOP原始碼分析

Spring之AOP原始碼分析

上一篇部落格簡單講了 spring-aop 的基礎元件、架構和使用方法,本文將開始研究 spring-aop 的原始碼,主要分成以下部分:

  1. spring-aop 的幾個重要的元件,如 Joinpoint、Advice、Pointcut、Advisor 等;
  2. spring-aop 是如何設計的

一點補充

在上一篇部落格中,我們使用 spring-aop 提供的代理工廠來生成動態代理類,被代理的物件可以是我們自己 new 的一個物件,也可以是 bean。因為 spring-aop 最主要的功能就是生成動態代理類,所以,本文的原始碼分析都只圍繞這個功能展開,並不會摻雜 spring-bean 的內容。

實際專案中,Spring 可以“悄無聲息”地完成對 bean 的代理,本質是通過註冊BeanPostProcessor來實現,原理並不複雜。如果你對 spring-bean 感興趣的話,可以參考部落格

幾個重要的元件

說到 spring-aop,我們經常會提到JoinpointAdvicePointcutAspectAdvisor等等概念,它們都是抽象出來的“標準”,有的來自 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 表示以上類的關係,如下。可以看到,這主要包含兩個部分:JoinpointAdvice(這是 AOP 最核心的兩個概念)。完整的 aopalliance 包,除了 aop 和 intercept,還包括了 instrument 和 reflect,後面這兩個部分 spring-aop 沒有引入,這裡就不說了。

  1. Joinpoint

Joinpoint表示對某個方法(構造方法或成員方法)或屬性的呼叫。

例如,我呼叫了 user.save() 方法,這個呼叫動作就屬於一個JoinpointJoinpoint是一個“動態”的概念,FieldMethodConstructor等物件是它的靜態部分。

如上圖所示,JoinpointAdvice操作的物件。

在 spring-aop 中,主要使用Joinpoint的子介面--MethodInvocation

  1. Advice

Joinpoint執行的某些操作。

例如,JDK 動態代理使用的InvocationHandler、cglib 使用的MethodInterceptor,在抽象概念上可以算是Advice(即使它們沒有繼承Advice)。

在 spring-aop 中,主要使用Advice的子介面--MethodInterceptor

為了更好地理解這兩個概念,我再舉一個例子:當我們對使用者進行新增操作前,需要進行許可權校驗。其中,呼叫 user.save() 的動作就是一個的Joinpoint,許可權校驗就是一個Advice,即對Joinpoint(新增使用者的動作)做Advice(許可權校驗)。

在 spring-aop 中,Joinpoint物件持有了一條 Advice chain ,呼叫Joinpointproceed()方法將採用責任鏈的形式依次執行各個 Advice(注意,Advice的執行可以互相巢狀,不是單純的先後順序)。cglib 和 JDK 動態代理的缺點就在於,它們沒有所謂的 Advice chain,一個Joinpoint一般只能分配一個Advice,當需要使用多個Advice時,需要像套娃一樣層層代理。

其他的幾個概念

在 spring-aop 中,還會使用到其他的概念,例如Advice FilterAdvisorPointcutAspect等。這些概念的重要性不如JoinpointAdvice,如果對深入瞭解 spring-aop 不感興趣的話,可以不用瞭解。

  1. Advice Filter

Advice Filter 一般和Advice繫結,它用來告訴我們,Advice是否作用於指定的Joinpoint,如果 true,則將Advice加入到當前JoinpointAdvice chain,如果為 false,則不加入。

在 spring-aop 中,常用的 Advice Filter 包括ClassFilterMethodMatcher,前者過濾的是類,後者過濾的是方法。

  1. Pointcut

Pointcut是 AspectJ 的元件,它是一種 Advice Filter。

在 spring-aop 中,Pointcut=ClassFilter+MethodMatcher+。

  1. Advisor

Advisor是 spring-aop 原創的元件,一個 Advisor = 一個 Advice Filter + 一個 Advice。

在 spring-aop 中,主要有兩種AdvisorIntroductionAdvisorPointcutAdvisor。前者為ClassFilter+Advice,後者為Pointcut+Advice

  1. 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 圖來簡單說明。

spring-aop 採用動態代理為目標類生成一個代理物件,Joinpoint 的組裝和 advice chain 的執行都是在這個代理物件中完成,而不是通過層層代理的方式來實現。

AdvisedSupport裡持有了一條 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 過濾得到,呼叫Joinpointproceed()方法就可以執行Advice chain

以上簡單介紹了 spring-aop 的設計檢視,有了這些,相信以後會更容易讀懂具體的原始碼。