SpringAOP的源碼解析

一、SpringAOP的概念

一、AOP的基本概念

1、連接點(Joinpoint):可以被增強的方法。

2、切點(Pointcut):實際被增強的方法。

3、通知(Advice)(增強):

  3.1.實際增強的邏輯部分叫做通知

  3.2.通知類型包括

  1. 前置通知(執行方法前執行,通常用作參數日誌輸出、許可權校驗等)
  2. 後置通知(邏輯程式碼執行完,準備執行return的程式碼時通知,通常用作執行結果日誌輸出、結果加密等)
  3. 環繞通知(是前置通知和後置通知的綜合,方法執行前和方法執行後都要執行,通常用作方法性能統計、介面耗時、統一加密、解密等)
  4. 異常通知(相當於try{}catch ()中catch執行的部分,程式拋出異常時執行,通常用作告警處理、事務回滾等)
  5. 最終通知(相當於try{}catch (Exception e){}finally { }中的finally執行的部分,通常用在關閉資源、清理快取等業務邏輯中)

4、切面(Aspect):把通知(增強)應用到切入點的過程。

二、Spring 框架一般都是基於 AspectJ 實現 AOP 操作

(1)AspectJ 不是 Spring 組成部分,獨立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使 用,進行 AOP 操作

三、基於 AspectJ 實現 AOP 操作

(1)基於 xml 配置文件實現

(2)基於註解方式實現(使用)

二、SpringAOP的使用

1.通過maven方式引用jar包

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.3.17</version>
    </dependency>

2.創建被代理介面和實現類。程式碼如下:

package com.ybe.aop;

public interface  Calculate {
    /**
     * 除法
     * @param numA
     * @param numB
     * @return
     */
    int div(int numA,int numB);
}

package com.ybe.aop.impl;

import com.ybe.aop.Calculate;

public class CalculateImpl implements Calculate {
  
    @Override
    public int div(int numA, int numB) {
        System.out.println("執行目標方法:div");
        return numA / numB;
    }
}

3.使用@Aspect註解創建切面類(Aspect),程式碼如下:

package com.ybe.aop.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Aspect
@Component
public class LogAspectj {

    @Pointcut("execution(* com.ybe.aop.impl.CalculateImpl.*(..))")
    private void pointCut(){
    }

    @Before(value = "pointCut()")
    public void methodBefore(JoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("執行目標方法【"+methodName+"】的<前置通知>,入參"+Arrays.asList(joinPoint.getArgs()));
    }

    @After(value = "pointCut()")
    public void methodAfter(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("執行目標方法【"+methodName+"】的<後置通知>,入參"+Arrays.asList(joinPoint.getArgs()));
    }

    @AfterReturning(value = "pointCut()",returning = "result")
    public void methodReturning(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("執行目標方法【"+methodName+"】的<返回通知>,入參"+Arrays.asList(joinPoint.getArgs()));
    }

    @AfterThrowing(value = "pointCut()")
    public void methodAfterThrowing(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("執行目標方法【"+methodName+"】的<異常通知>,入參"+Arrays.asList(joinPoint.getArgs()));
    }

}

4.創建Config 配置類,使用註解@EnableAspectJAutoProxy開啟aop功能,程式碼如下:

package com.ybe.aop.config;

import com.ybe.aop.Calculate;
import com.ybe.aop.impl.CalculateImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan("com.ybe.aop")
@EnableAspectJAutoProxy
public class Config {
    @Bean
    public Calculate calculate(){
        return new CalculateImpl();
    }
}

5.Main的程式碼

 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
 Calculate proxyFactoryBean = context.getBean("calculate", Calculate.class);
 context.div(1,1);

6.運行結果如下:

三、SpringAOP的源碼分析

SpringAop實現利用了SpringIoc容器。在SpringIOC容器的生命周期過程中整合了SpringAOP的功能。大概過程:通過 @Import註冊 實現了ImportBeanDefinitionRegistrar 介面的 AspectJAutoProxyRegistrar 類。在該類中添加實現了 InstantiationAwareBeanPostProcessor 介面的 AnnotationAwareAspectJAutoProxyCreator 類。在創建AnnoteationConfigApplicationContext的構造函數中會調用refresh()方法。refresh方法會進行 AspectJAutoProxyRegistrar 的調用,並且生成

AnnotationAwareAspectJAutoProxyCreator 的Bean對象。在第一次調用 CreateBean 的時候,進行Advisors的創建。在創建完 Bean後會調用AnnotationAwareAspectJAutoProxyCreator的 postProcessAfterInitialization方法。從 Advisors 中查找是否匹配當前正在創建的Bean。如果能匹配,則創建相關的動態代理對象。

完整源碼分析分三部分:SpringAOP的初始化、創建動態代理、代理方法調用過程。

一、SpringAOP的初始化。

主要邏輯是找到所有標註了 @Aspect 的類,並且解析類中所有的通知方法並添加到 BeanFactoryAspectJAdvisorsBuilder.advisorsCache 快取中。

整體程式碼流程圖如下:

說明:

  1. 創建 AnnotationConfigApplicationContext() 容器。

  2. 在invokeBeanFactoryPostProcessors()中,會調用 ConfigurationClassPostProcessor 的 postProcessBeanDefinitionRegistry() 。在此方法中,會找到 @EnableAspectJAutoProxy 的 @Import 屬性傳入的 AspectJAutoProxyRegistrar.class 類。並且執行該類的registerBeanDefinitions() 方法,創建類型為 AnnotationAwareAspectJAutoProxyCreator 、名稱為org.springframework.aop.
    config.internalAutoProxyCreator的 RootBeanDefinition註冊到BeanDefinitionRegistry中。

  3. 在 registerBeanPostProcessors() 中會根據上面一步生成的 RootBeanDefinition對象創建 AnnotationAwareAspectJAutoProxyCreator 的實例。

  4. 在 finishBeanFactoryInitialization() 中第一次執行到 AbstractAutowireCapableBeanFactory.createBean() 時,會執行一段這樣的程式碼,如下

    try {
            // 讓 BeanPostProcessors 有機會返回一個代理而不是目標 bean 實例
            Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
            if (bean != null) {
                return bean;
            }
    }
    
    @Nullable
    protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
    		Object bean = null;
    		if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
    			// Make sure bean class is actually resolved at this point.
    			if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
    				Class<?> targetType = determineTargetType(beanName, mbd);
    				if (targetType != null) {
    					bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
    					if (bean != null) {
    						bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
    					}
    				}
    			}
    			mbd.beforeInstantiationResolved = (bean != null);
    		}
    		return bean;
    }
    
    @Nullable
    protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
    	    for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
                 Object result = bp.postProcessBeforeInstantiation(beanClass, beanName);
                 if (result != null) {
                   return result;
                 }
               }
             return null;
    }
    

    以上程式碼會執行 AnnotationAwareAspectJAutoProxyCreator 的 postProcessBeforeInstantiation() 方法。在該方法中會 執行 shouldSkip() 方法。程式碼如下:

    @Override
    protected boolean shouldSkip(Class<?> beanClass, String beanName) {
        // TODO: Consider optimization by caching the list of the aspect names
     // 找到所有候選的 Advisors
     List<Advisor> candidateAdvisors = findCandidateAdvisors();
     for (Advisor advisor : candidateAdvisors) {
         if (advisor instanceof AspectJPointcutAdvisor &&
             ((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
             return true;
         }
     }
     return super.shouldSkip(beanClass, beanName);
    }
    

    在 findCandidateAdvisors 中具體會生成所有的 Advisors。

    @Override
    protected List<Advisor> findCandidateAdvisors() {
    		// 找到所有的 實現了 Advisor.class 介面的類,並且生成候選的 Advisors.
    		List<Advisor> advisors = super.findCandidateAdvisors();
    		// 創建所有的帶了 @Aspect 特性的切面類 .
    		if (this.aspectJAdvisorsBuilder != null) {
    			advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
    		}
    		return advisors;
    }
    

    aspectJAdvisorsBuilder.buildAspectJAdvisors() 是核心。方法裡面的邏輯如下:

    1.獲取容器里所有的beanNames.
    2.遍歷 beanNames,根據beanName獲取對應的beanType對象。
    3.判斷beanType是否有@Aspect註解。
    4.如果有,調用getAdvisorMethods()通過反射獲取該類型所有的 advisor 的 method 元數據。
    5.遍歷 methods 調用 getAdvisor() 獲取 Advisor 對象(InstantiationModelAwarePointcutAdvisorImpl)
    6.添加到 this.advisorsCache 中。
    

​ postProcessBeforeInstantiation方法會快取所有的advisor,方法的最後返回 null。至此整個 SpringAOP的初始化完成。

二、創建動態代理

​ 在創建Bean的生命周期的 initializeBean 方法中,會執行 AnnotationAwareAspectJAutoProxyCreator的 postProcessAfterInitialization方法。該方法會拿快取BeanFactoryAspectJAdvisorsBuilder.advisorsCache 中所有advisor的pointCut去匹配正在創建的實例Bean的所有方法。如果 advisor 和 Bean 的某一個方法能匹配上,則把該advisor添加到 advisor的候選集合中。直到找出匹配Bean的所有Adsivors。最後根據Adsivor的候選集合和Bean類型創建動態代理對象ProxyFactory。

整體程式碼流程圖如下:

說明:

1.List排序後的順序為:

ExposeInvocationInterceptor

Around

Before

After

AfterReturning

AfterThrowing

2.動態代理的創建

創建動態代理有兩種方法,一種是 JDK ,一種是 CGLib 。

1.如果目標類有實現介面的話,則是使用JDK的方式生成代理對象。

2.配置了使用Cglib進行動態代理或者目標類沒有實現介面,那麼使用Cglib的方式創建代理對象。

三、動態代理調用

以 JdkDynamicAopProxy 為例,在調用方法的時候會直接調用 JdkDynamicAopProxy.invoke()方法,裡面的大概邏輯如下:

1.獲取被代理的實現類;

2.找出所有匹配被調用方法的 advisor,並且轉成具體的通知攔截器 MethodInterceptor,返回通知攔截器鏈。轉換程式碼如下:

List<MethodInterceptor> interceptors = new ArrayList<>(3);
// 從Advisor中獲取 Advice
Advice advice = advisor.getAdvice();
// 如果 advice 本身就實現了  MethodInterceptor 介面 ,則直接進行轉換
if (advice instanceof MethodInterceptor) {
    interceptors.add((MethodInterceptor) advice);
}
// AfterReturningAdviceInterceptor MethodBeforeAdviceInterceptor  ThrowsAdviceInterceptor 
// 這三種是通過適配器的方式進行轉換 MethodInterceptor類型
for (AdvisorAdapter adapter : this.adapters) {
    if (adapter.supportsAdvice(advice)) {
        interceptors.add(adapter.getInterceptor(advisor));
    }
}
if (interceptors.isEmpty()) {
    throw new UnknownAdviceTypeException(advisor.getAdvice());
}
return interceptors.toArray(new MethodInterceptor[0]);

3.創建 ReflectiveMethodInvocation 對象(該對象中包括了 代理對象、被代理對象、執行的方法、方法參數、被代理對象的類型、通知攔截器鏈),執行該對象的proceed()方法,該方法中會進行通知攔截器鏈的遞歸調用,具體調用流程如下圖。ReflectiveMethodInvocation 對象在通知攔截器鏈調用中作用很關鍵,有銜接各個攔截器的作用。

程式碼流程如下圖:

說明:

1.在proceed方法中,會先判斷當前攔截器鏈的索引,如果索引等於最後一個那麼則執行被代理類的方法。

2.如果不是,那麼先獲取該通知攔截器並且執行該攔截器的 proceed 方法(方法接受 ReflectiveMethodInvocation 對象實例),每個通知攔截器中都會調用 ReflectiveMethodInvocation 對象實例 的proceed 方法。在這裡會形成遞歸調用。

3.通知攔截器的排序請看下圖:

4.五個通知攔截器的程式碼解釋請看上面的程式碼流程圖。

Tags: