天天看點

Spring AOP源碼-代理生産過程前言一、AOP 名詞介紹?二、AOP執行順序二、AOP 源碼分析從何入手三、AOP配置解析四、AOP代理類生成五、總結附錄

目錄

  • 前言
  • 一、AOP 名詞介紹?
  • 二、AOP執行順序
  • 二、AOP 源碼分析從何入手
    • 1.AOP核心類介紹
      • 介紹一些Spring Aop中一些核心類,大緻分為三類:
    • 3.Spring啟動中AOP大緻流程
  • 三、AOP配置解析
  • 四、AOP代理類生成
  • 五、總結
  • 附錄

前言

在分析 Spring AOP 源碼之前,如果你對 Spring IOC、依賴注入(DI) 原理不是很清楚,建議您先了解一下:Spring IOC 源碼解析、Spring 依賴注入(DI) 源碼解析,這樣或許會讓你的思路更加清晰。

一、AOP 名詞介紹?

在AOP中,共涉及到

**1.切面 Aspect**

**2.連接配接點 JoinPoint**

**3.通知 Advice**

**4.切入點 Pointcut**

**5.目标對象 Target Object**

這 5 個名詞。

二、AOP執行順序

Spring5 AOP順序

從Spring5.2.7開始,Spring AOP不再嚴格按照AspectJ定義的規則來執行advice,而是根據其類型按照從高到低的優先級進行執行:@Around,@Before ,@After,@AfterReturning,@AfterThrowing。

Spring AOP源碼-代理生産過程前言一、AOP 名詞介紹?二、AOP執行順序二、AOP 源碼分析從何入手三、AOP配置解析四、AOP代理類生成五、總結附錄

二、AOP 源碼分析從何入手

1.AOP核心類介紹

介紹一些Spring Aop中一些核心類,大緻分為三類:

Spring AOP源碼-代理生産過程前言一、AOP 名詞介紹?二、AOP執行順序二、AOP 源碼分析從何入手三、AOP配置解析四、AOP代理類生成五、總結附錄

(1)AbstractAutoProxyCreator:繼承 spring ioc的擴充接口 BeanPostProcessor,主要用來掃描擷取 advisor。BeanPostProcessor作用: Spring容器中完成bean執行個體化、配置以及其他初始化方法前後要添加一些自己邏輯處理。

AbstractAdvisorAutoProxyCreator:預設掃描所有Advisor的實作類。相對于根據Bean名稱比對,該類更加靈活。動态的比對每一個類,判斷是否可以被代理,并尋找合适的增強類,以及生成代理類。

AnnotationAwareAspectJAutoProxyCreator:目前最常用的AOP使用方式。spring aop 開啟注解方式之後,該類會掃描所有@Aspect()注釋的類,生成對應的advisor。目前SpringBoot架構中預設支援的方式,自動配置。

(2) Advisor:顧問的意思,封裝了spring aop中的切點和通知。 就是我們常用的@Aspect 注解标記得類

(3) Advice:通知,也就是aop中增強的方法。

3.Spring啟動中AOP大緻流程

@Override
public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {

		// Allows post-processing of the bean factory in context subclasses.
		postProcessBeanFactory(beanFactory);

		// AnnotationAwareAspectJAutoProxyCreator
		invokeBeanFactoryPostProcessors(beanFactory);

		// 擷取@Aspect标記的類,生成Advicsor,加入緩存
		registerBeanPostProcessors(beanFactory);
		…………
		// 擷取容器中所有的bean後置處理器集合周遊每個processor,将bean變成代理對象
		finishBeanFactoryInitialization(beanFactory);

		// Last step: publish corresponding event.
		finishRefresh();
	}
}
           

下面講根據Spring啟動流程,分成兩個階段講解AOP源碼,第一個階段AOP配置解析,第二個階段容器中單執行個體Bean(需AOP)的建立過程

三、AOP配置解析

使用注解版的AOP實作切面功能時,AOP功能如果要生效,必須先在配置類上使用注解開啟AOP支援,使用到的注解就是**@EnableAspectJAutoProxy**,配置類如下:

@Configuration
@ComponentScan(basePackages = "com.wb.spring.aop")
// 開啟AOP支援
@EnableAspectJAutoProxy
public class AopConfig {
}
           

一、AOP的前期初始化過程

1.1、後置處理器初始化過程分析

那麼**@EnableAspectJAutoProxy**注解究竟幹了什麼事呢?打開其源碼實作,如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
// 通過Import注解給容器中導入AspectJAutoProxyRegistrar元件.
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
   /**
    * 用來指定是否使用CGLIB的方式來建立代理
    * 預設值為false,表示使用基于接口的JDK動态代理方式來建立
    */
   boolean proxyTargetClass() default false;
   /**
    *用來設定是否将代理類暴露到AopContext中。如果将代理暴露在AopContext中,
    *代理類将會被儲存在ThreadLocal中,在需要使用代理的時候直接從ThreadLocal中擷取。
    */
   boolean exposeProxy() default false;
}
           

​ 通過源碼可以發現,在**@EnableAspectJAutoProxy注解中,又使用了@Import注解給容器中導入了一個AspectJAutoProxyRegistrar**元件(關于@Import注解之前文章中已經詳細介紹過,此處不再介紹),那麼這個元件又是幹什麼的呢?繼續打開其源碼,如下:

// 實作了ImportBeanDefinitionRegistrar接口,用來在Spring啟動時給bean注冊中心自定義注冊元件
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
   @Override
   public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
      // 如果有需要,則給容器中注冊一個AspectJAnnotationAutoProxyCreator元件.
      AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
      // 擷取EnableAspectJAutoProxy注解上标注的屬性值
      AnnotationAttributes enableAspectJAutoProxy =
            AnnotationConfigUtils.attributesFor(importingClassMetadata, 
                    EnableAspectJAutoProxy.class);
      // ... 其他源碼暫時省略
   }
}
           

這個元件是給容器的bean定義注冊中心自定義注冊一個bean元件,通過調用AOP工具類中AopConfigUtils中的registerAspectJAnnotationAutoProxyCreatorIfNecessary方法完成。

​ 繼續檢視方法registerAspectJAnnotationAutoProxyCreatorIfNecessary的實作,内容總共分為如下的三步,最終的效果就是給容器中注冊一個名稱為internalAutoProxyCreator,類型為AnnotationAwareAspectJAutoProxyCreator的bean元件。

public abstract class AopConfigUtils {
  // 第一步:調用該方法給容器中注冊一個bean元件
  @Nullable
  public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
     return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
  }
  // 第二步:再次調用該方法,傳入bean元件的類型為:AnnotationAwareAspectJAutoProxyCreator的bean元件
  @Nullable
  public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
        BeanDefinitionRegistry registry, @Nullable Object source) {
      return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
  }
  /**
   * 第二步:最後調用這個方法給容器中注冊一個名稱為org.springframework.aop.config.internalAutoProxyCreator,
   *   類型為AnnotationAwareAspectJAutoProxyCreator的元件
   */
  @Nullable
  private static BeanDefinition registerOrEscalateApcAsRequired(
      Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
    // cls = AnnotationAwareAspectJAutoProxyCreator.class
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");

    // 判斷容器中是否有名稱為org.springframework.aop.config.internalAutoProxyCreator的Bean定義,第一次運作的時候沒有.
    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
      BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
      if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
        int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
        int requiredPriority = findPriorityForClass(cls);
        if (currentPriority < requiredPriority) {
          apcDefinition.setBeanClassName(cls.getName());
        }
      }
      return null;
    }
    // 建立一個AnnotationAwareAspectJAutoProxyCreator類型的Bean定義
    RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
    beanDefinition.setSource(source);
    // 設定最高優先級.
    beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
    beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    // 設定bean定義的名稱為:‘org.springframework.aop.config.internalAutoProxyCreator’
    registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
    return beanDefinition;
  }
  // ... 工具類中的其他代碼暫時省略.
}
           

是以,重點就轉移到了這個類型為AnnotationAwareAspectJAutoProxyCreator(上面所述AOP核心類)的bean元件,這個元件的功能及運作時機搞清楚,整個AOP的原理就清楚了,後續的其他類似使用注解@Enablexxx實作的功能,例如注解版事務@EnableTransactionManagement,套路都是一樣,都是給容器中注冊一些元件,然後在某個時機執行對應的方法。深究其底層原理其實通過檢視該注解給容器中注入的元件就可以了。

AnnotationAwareAspectJAutoProxyCreator這個類最終是實作了BeanPostProcessor和Aware接口的,Spring的一個最強大的功能就是可以支援靈活擴充,提供了很多擴充點,這兩個接口就是Spring中提供的兩個重要擴充點,而且這兩個擴充點對應的方法會在Bean的建立過程中被調用。是以,重點再一次轉移到和這些後置處理器相關的方法上,檢視後置處理器如何完成調用的。

1.2、後置處理器初始化的調用過程

(1)在refresh方法中調用registerBeanPostProcessors方法給容器中注冊後置處理器,這個過程中,就會注冊與Aop相關的後置處理器AnnotationAwareAspectJAutoProxyCreator;

(2)在注冊AnnotationAwareAspectJAutoProxyCreator的過程中,會通過Spring的Bean建立過程去完成後置處理器Bean元件的執行個體化,屬性指派及初始化的操作。執行個體化即通過反射的方式去建立對象;填充屬性,即通過内省操作對象的setter方法完成;初始化操作中,又分為如下幾個小步驟:

invokeAwareMethods(beanName, bean)

,主要是用來調用與Aware接口有關的方法,因為這個AOP對應的後置處理器實作了BeanFactoryAware接口,是以會調用其setBeanFactory方法;

applyBeanPostProcessorsBeforeInitialization

,在bean初始化之前調用,可以再bean建立之前做一些自定義的操作,因為AOP對應的後置處理器實作了BeanPostProcessor接口,是以會調用該方法;

//擷取Adivsor
public List<Advisor> buildAspectJAdvisors() {
		List<String> aspectNames = this.aspectBeanNames;	
		if (aspectNames == null) {
		synchronized (this) {
			aspectNames = this.aspectBeanNames;
			if (aspectNames == null) {
				List<Advisor> advisors = new ArrayList<>();
				aspectNames = new ArrayList<>();
        //1、擷取到容器中所有對象的beanNames集合(非常耗時)
        //2、周遊每個beanName
        //3、擷取beanName對應類對象
				String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
						this.beanFactory, Object.class, true, false);
				for (String beanName : beanNames) {
					if (!isEligibleBean(beanName)) {
						continue;
					}
					Class<?> beanType = this.beanFactory.getType(beanName);
					if (beanType == null) {
						continue;
					}
					if (this.advisorFactory.isAspect(beanType)) {
            //① 收集beanName
						aspectNames.add(beanName);
						AspectMetadata amd = new AspectMetadata(beanType, beanName);
						if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
              //② 建構工廠對象
							MetadataAwareAspectInstanceFactory factory =
									new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
              //③ 生成增強器
							List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
              //④ 加入緩存
							if (this.beanFactory.isSingleton(beanName)) {
								this.advisorsCache.put(beanName, classAdvisors);
							}
							else {
								this.aspectFactoryCache.put(beanName, factory);
							}
							advisors.addAll(classAdvisors);
						}
						else {
							//省略
						}
					}
				}
				this.aspectBeanNames = aspectNames;
				return advisors;
			}
		}
	}
	//省略
	return advisors;
}
           

invokeInitMethods

,執行自定義的初始化方法,例如:init-method指定的方法或者如果bean實作了InitializingBean接口,則在該步驟中會執行afterPropertiesSet方法;

applyBeanPostProcessorAfterInitialization

,在bean初始化之後調用,可以再bean初始化之後,再做一些自定義操作,由于AOP對應的後置處理器實作了BeanPostProcessor接口,是以該方法在Bean初始化完成之後也會被調用,Spring建立AOP的代理對象就是在該步驟中完成的,通常有兩種方式:cglib和JDK動态代理;

(3)經過上述一系列操作之後,AnnotationAwareAspectJAutoProxyCreator後置處理器就被建立完成,然後将建立完成的後置處理器元件加入到Spring容器中的beanFactory對象中。

上述整個過程調用鍊比較深,代碼也比較多。篇幅有限,就不貼代碼了,在看源碼的過程中,可以根據上述的過程去一步步檢視。

1.3、容器中單執行個體Bean的建立過程

​ 上述過程完成之後,隻會給容器中注冊一個AnnotationAwareAspectJAutoProxyCreator類型的後置處理器。但是容器中需要被增強的Bean示例還未建立出來,那麼這個Bean示例是在什麼時候被建立的呢?下面分析一下其建立過程。

(1)在容器重新整理refresh過程的倒數第二步中會完成容器中剩餘的單執行個體Bean的建立及初始化。即:**finishBeanFactoryInitialization(beanFactory)**方法。

(2)而在建立bean的過程中,會受到容器中後置處理器的攔截操作,包括上述第一步給容器中注冊的AnnotationAwareAspectJAutoProxyCreator後置處理器,會在Bean建立前後及初始化前後執行後置處理器方法,對Bean做一些自定義的攔截操作,包括對bean進行包裝,生成對應的代理對象;

進入AbstractAutoProxyCreator(抽象類)

  1. 容器中查找 Advisor類型(事務)
  2. 容器中查找注解了@aspectj的bean(重點)

(3)在建立TestController(檢視附錄)對應的bean時,被後置處理器攔截到之後,會執行如下的處理過程,具體建立代理對象的過程在postProcessAfterInitialization方法中:

① 判斷目前bean是否已經被增強過,如果已經被增強,則會直接傳回這個bean;判斷依據就是根據bean的名稱判斷已經增強的bean對應的Map集合中是否包括目前需要建立的bean;

② 如果目前bean未被增強過,則去判斷目前bean是否需要被包裝,如果不需要被包裝,則直接傳回原來的bean,如果需要被包裝,則會通過AbstractAutoProxyCreator的wrapIfNecessary方法進行包裝;

③ 如果需要被包裝,即執行了wrapIfNecessary方法,則會先去擷取所有的增強,如果能夠擷取到增強器,則會調用AbstractAutoProxyCreator的createProxy方法去建立代理對象。

AbstractAutoProxyCreator.createProxy方法的建立邏輯:

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
      @Nullable Object[] specificInterceptors, TargetSource targetSource) {
   ProxyFactory proxyFactory = new ProxyFactory();
   proxyFactory.copyFrom(this);
   // 其他不相關的代碼略...
   
   // 預設不指定生成代理的方式,則proxyTargetClass為false,使用的是jdk動态代理
   // 如果未強制指定使用cglib生成動态代理,則會去校驗目前接口是否能夠正常生成代理對象
   if (!proxyFactory.isProxyTargetClass()) {
      // 目前bean定義中指定了preserveTargetClass屬性為true,則會強制使用cglib動态代理
      if (shouldProxyTargetClass(beanClass, beanName)) {
         proxyFactory.setProxyTargetClass(true);
      }
      else {
         // 判斷目前bean實作的接口是否為标記類的接口
         //   如果為标記類的接口,例如:Aware接口,則還是會強制使用cglib去生成代理
         evaluateProxyInterfaces(beanClass, proxyFactory);
      }
   }
   /** 找到所有的advisors增強點,即:環繞,前置,後置,傳回,異常等通知方法 */
   Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
   /** 設定增強點 */
   proxyFactory.addAdvisors(advisors);
   /** 設定需要代理的目标類 */
   proxyFactory.setTargetSource(targetSource);
   /** 定制代理工廠,可以使用自定義的代理工廠,此處使用的還是預設的DefaultAopProxyFactory工廠 */
   customizeProxyFactory(proxyFactory);
   proxyFactory.setFrozen(this.freezeProxy);
   if (advisorsPreFiltered()) {
      proxyFactory.setPreFiltered(true);
   }
   /** 建立代理對象,裡面會判斷使用JDK動态代理還是CGLIB動态代理 */
   return proxyFactory.getProxy(getProxyClassLoader());
}
           

建立代理的時候會根據條件去決定是使用cglib建立代理還是根據jdk去建立代理,如下DefaultAopProxyFactory的createAopProxy方法,該方法源碼如下:

@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
   // isOptimize:表示是否需要對生成代理的政策進行優化,可以在配置中指定
   // isProxyTargetClass:表示是否需要強制使用cglib來生成代理,預設為false,通常都會指定強制使用cglib,即将該值設定為true
   // (目前被代理的類是否未實作接口,實作的接口數為0)或者(是否實作了SpringProxy接口)
   if (config.isOptimize() || config.isProxyTargetClass() 
            || hasNoUserSuppliedProxyInterfaces(config)) {
      // 擷取需要被代理的目标類
      Class<?> targetClass = config.getTargetClass();
      if (targetClass == null) {
         throw new AopConfigException("TargetSource cannot determine target class: " +
               "Either an interface or a target is required for proxy creation.");
      }
      // 如果要代理的目标類為接口,則直接使用jdk動态代理
      if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
         return new JdkDynamicAopProxy(config);
      }
      // 否則使用cglib動态代理建立對象
      return new ObjenesisCglibAopProxy(config);
   }
   // 如果建立代理時不需要優化,未指定使用cglib,而且存在接口,則直接使用jdk建立代理對象
   else {
      return new JdkDynamicAopProxy(config);
   }
}
           

此處傳回的就是一個通過政策類建立的代理對象,使用了【政策設計模式】,根據不同的條件使用不同的實作政策去建立代理對象,包括ObjenesisCglibAopProxy對應的政策實作以及JdkDynamicAopProxy對應的政策實作,他們都是AopProxy政策接口的實作類。

1.4、Spring生成代理的方式

Spring底層同時支援了兩種生成代理的方式,那麼cglib動态代理和jdk的動态代理究竟有什麼差別呢?如果選擇呢?

(1)使用方式不同

JDK動态代理的前提是被代理的類必須實作了某一個接口,而cglib不需要強制要求被代理類實作接口,可以是接口或者實作類。

(2)生成位元組碼的方式不同

JDK動态代理和cglib動态代理都是運作期間為被代理對象生成位元組碼,JDK是直接操作位元組碼,而cglib是使用了asm架構操作位元組碼。

(3)生成代理的效率不同

JDK生成位元組碼是直接使用接口,邏輯比較簡單,效率稍高。而cglib生成位元組碼的邏輯比較複雜,是以生成代理的效率比較低。

(4)執行效率不同

JDK調用代理方法時,是需要通過反射調用,而cglib是通過fastClass機制直接調用方法,執行效率更高。

那麼什麼又是FastClass機制呢?為什麼FastClass就這麼快呢?

cglib執行代理方法的效率比jdk高,是因為cglib采用了fastClass機制,而JDK是通過反射調用的。

FastClass的原理:生成代理類時,為代理類和被代理類各生成一個Class,這個Class會為代理類或者被代理類的方法配置設定一個int類型的索引index,調用的時候将這個index當做一個入參,FastClass就可以直接通過索引定位到要調用的方法直接進行調用,是以省略了反射調用,執行效率高于JDK。使用類似于資料庫索引的設計思想。

經過上面的buildAspectJAdvisors方法,擷取了Advisor的緩存Map

四、AOP代理類生成

// 擷取需要增強的Advisor
	Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
	if (specificInterceptors != DO_NOT_PROXY) {
		this.advisedBeans.put(cacheKey, Boolean.TRUE);
		Object proxy = createProxy(
				bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
		this.proxyTypes.put(cacheKey, proxy.getClass());
		return proxy;
	}

	this.advisedBeans.put(cacheKey, Boolean.FALSE);
	return bean;
}
           

五、總結

Spring的AOP初始化過程比較複雜,将整體的大思路可以總結為如下幾個步驟:

(1)注冊後置處理器

給容器中導入一個用于攔截Bean床架的後置處理器,利用了Spring的擴充接口Aware及BeanPostProcessor接口。在注解版中,是通過@EnableAspectJAutoProxy來導入的,而在傳統的xml中類似,隻不過是通過配置的方式,但最終還是通過Spring的xml解析器将xml中配置的内容解析為了bean定義,并注冊到Spring容器中了;

(2)建立增強之後的Bean

在容器啟動的時候,會去建立所有使用注解标注的Bean元件,例如:@Component注解,而且會解析出标有@Aspect注解的切面類,并解析出類中定義的切點表達式。然後在初始化其他bean的時候,會根據切點表達式去比對目前類是否需要增強,如果需要增強,則會對目前的類建立代理對象,建立代理對象是在Bean初始化完成之後做的;

(3)建立增強Bean的方式

在後置處理器中建立增強Bean時,會根據目前類是否實作了接口,代理類是否需要被優化,實作的接口是否為Spring原生的标記類接口,是否強制使用cglib方式等等條件去決定是使用cglib的方式還是jdk的方式,最後通過AopProxy政策接口的具體政策實作類去建立對應的代理類,然後加入到Spring容器中,整個初始化過程就執行完畢。

最後附上AOP流程圖

https://www.processon.com/view/link/611bbd661efad412479f8387

Spring AOP源碼-代理生産過程前言一、AOP 名詞介紹?二、AOP執行順序二、AOP 源碼分析從何入手三、AOP配置解析四、AOP代理類生成五、總結附錄

附錄

@RestController
public class TestController {
    @GetMapping("hello")
    public String hello() {
        return "hello spring security";
    }
}
           
@Aspect
@Component
public class AOPDemo {
    @Before("execution(* *.hello(..))")
    public void beforeNotify() {

        System.out.println("********@Before我是前置通知");
    }

    @After("execution(* *.hello(..))")
    public void afterNotify() {

        System.out.println("********@After我是後置通知");
    }

    @AfterReturning("execution(* *.hello(..))")
    public void afterReturningNotify() {

        System.out.println("********@AfterReturning我是傳回後通知");
    }

    @AfterThrowing(" execution(* *.hello(..))")
    public void afterThrowingNotify() {

        System.out.println("********@AfterThrowing我是異常通知");
    }

    @Around(" execution(* *.hello(..))")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

        Object retvalue = null;
        System.out.println("我是環繞通知之前AAA");
        retvalue = proceedingJoinPoint.proceed();
        System.out.println("我是環繞通知之後BBB");
        return retvalue ;
    }
}
           

繼續閱讀