【Spring註解驅動開發】二狗子讓我給他講講@EnableAspectJAutoProxy註解

冰河團隊發表於2020-09-02

寫在前面

最近,二狗子入職了新公司,新入職的那幾天確實有點飄。不過慢慢的,他發現他身邊的人各個身懷絕技啊,有Spring原始碼的貢獻者,有Dubbo原始碼的貢獻者,有MyBatis原始碼的貢獻者,還有研究AI的大佬,個個都是大神級別的人物。二狗子有點慌,想起自己雖然入職了,但是比起其他人確實差點遠啊。怎麼辦呢?先從基礎補起唄,他發現自己對於Spring的理解還不算太深。於是乎,他讓我給他講講Spring的@EnableAspectJAutoProxy註解。

好吧,二狗子要請我吃飯啊!關注 冰河技術 微信公眾號,後臺回覆“Spring註解”領取工程原始碼。

如果文章對你有點幫助,請點個贊,給個在看和轉發,大家的三連是我持續創作的最大動力!

@EnableAspectJAutoProxy註解

在配置類上新增@EnableAspectJAutoProxy註解,能夠開啟註解版的AOP功能。也就是說,AOP中如果要使註解版的AOP功能起作用,就需要在配置類上新增@EnableAspectJAutoProxy註解。 我們先來看下@EnableAspectJAutoProxy註解的原始碼,如下所示。

package org.springframework.context.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
	boolean proxyTargetClass() default false;
	boolean exposeProxy() default false;
}

從原始碼可以看出,@EnableAspectJAutoProxy使用@Import註解引入了AspectJAutoProxyRegister.class物件 。那麼,AspectJAutoProxyRegistrar又是什麼呢?我們繼續點選到AspectJAutoProxyRegistrar類的原始碼中,如下所示。

package org.springframework.context.annotation;
import org.springframework.aop.config.AopConfigUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
	@Override
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

		AnnotationAttributes enableAspectJAutoProxy =
				AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
		if (enableAspectJAutoProxy != null) {
			if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
				AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
			}
			if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
				AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
			}
		}
	}
}

可以看到AspectJAutoProxyRegistrar類實現了ImportBeanDefinitionRegistrar介面。看下ImportBeanDefinitionRegistrar介面的定義,如下所示。

package org.springframework.context.annotation;

import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.core.type.AnnotationMetadata;
public interface ImportBeanDefinitionRegistrar {

	default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
			BeanNameGenerator importBeanNameGenerator) {

		registerBeanDefinitions(importingClassMetadata, registry);
	}

	default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
	}

看到ImportBeanDefinitionRegistrar介面,小夥伴們是不是覺得很熟悉呢。沒錯,我們在【Spring註解驅動開發】專題前面的文章中介紹過。可以通過ImportBeanDefinitionRegistrar介面實現將自定義的元件新增到IOC容器中。

也就說,@EnableAspectJAutoProxy註解使用AspectJAutoProxyRegistrar物件自定義元件,並將相應的元件新增到IOC容器中。

除錯Spring原始碼

我們在AspectJAutoProxyRegistrar類的registerBeanDefinitions()方法中設定斷點,如下所示。

接下來,我們以debug的方法來執行AopTest類的testAop01()方法。執行後程式進入到斷點位置,如下所示。

可以看到,程式已經暫停在斷點位置,而且在IDEA的左下角顯示了方法的呼叫棧。

在AspectJAutoProxyRegistrar類的registerBeanDefinitions()方法,首先呼叫AopConfigUtils類的registerAspectJAnnotationAutoProxyCreatorIfNecessary()方法來註冊registry。單看registerAspectJAnnotationAutoProxyCreatorIfNecessary()方法也不難理解,字面含義就是:如果需要的話註冊一個AspectJAnnotationAutoProxyCreator。

接下來,我們進入到AopConfigUtils類的registerAspectJAnnotationAutoProxyCreatorIfNecessary()方法中,如下所示。

在AopConfigUtils類的registerAspectJAnnotationAutoProxyCreatorIfNecessary()方法中,直接呼叫了過載的registerAspectJAnnotationAutoProxyCreatorIfNecessary()方法,我們繼續跟程式碼,如下所示。

可以看到在過載的registerAspectJAnnotationAutoProxyCreatorIfNecessary()方法中直接呼叫了registerOrEscalateApcAsRequired()方法。在registerOrEscalateApcAsRequired()方法中,傳入了AnnotationAwareAspectJAutoProxyCreator.class物件。

我們繼續跟進程式碼,如下所示。

我們可以看到,在registerOrEscalateApcAsRequired()方法中,接收到的Class物件的型別為:org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator。

在registerOrEscalateApcAsRequired()方法中方法中,首先判斷registry是否包含org.springframework.aop.config.internalAutoProxyCreator型別的bean。如下所示。

如果registry中包含org.springframework.aop.config.internalAutoProxyCreator型別的bean,則進行相應的處理,從Spring的原始碼來看,就是將org.springframework.aop.config.internalAutoProxyCreator型別的bean從registry中取出,並且判斷cls物件的name值和apcDefinition的beanClassName值是否相等,如果不相等。則獲取apcDefinition和cls的優先順序,如果apcDefinition的優先順序小於cls的優先順序,則將apcDefinition的beanClassName設定為cls的name值。相對來說,理解起來還是比較簡單的。

我們這裡是第一次執行程式,不會進入到 if 條件中,我們繼續看程式碼,如下所示。

這裡,使用RootBeanDefinition來建立一個beanDefinition,並且將org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator的Class物件作為引數傳遞進來。

我們繼續往下看程式碼,最終AopConfigUtils類的registerOrEscalateApcAsRequired()方法中,會通過registry呼叫registerBeanDefinition()方法註冊元件,如下所示。

並且註冊的bean的名稱為org.springframework.aop.config.internalAutoProxyCreator。

接下來,我們繼續看AspectJAutoProxyRegistrar類的registerBeanDefinitions()原始碼,如下所示。

通過AnnotationConfigUtils類的attributesFor方法來獲取@EnableAspectJAutoProxy註解的資訊。接下來,就是判斷proxyTargetClass屬性的值是否為true,如果為true則呼叫AopConfigUtils類的forceAutoProxyCreatorToUseClassProxying()方法;繼續判斷exposeProxy屬性的值是否為true,如果為true則呼叫AopConfigUtils類的forceAutoProxyCreatorToExposeProxy()方法。

綜上,向Spring的配置類上新增@EnableAspectJAutoProxy註解後,會向IOC容器中註冊AnnotationAwareAspectJAutoProxyCreator。

接下來,我們來看下AnnotationAwareAspectJAutoProxyCreator類的結構圖。

我們簡單梳理下AnnotationAwareAspectJAutoProxyCreato類的核心繼承關係,如下所示。

  AnnotationAwareAspectJAutoProxyCreator
       --AspectJAwareAdvisorAutoProxyCreator
         --AbstractAdvisorAutoProxyCreator
           --AbstractAutoProxyCreator
             -- ProxyProcessorSupport, SmartInstantiationAwareBeanPostProcessor

檢視繼承關係可以發現,此類實現了Aware與BeanPostProcessor介面,這兩個介面都和Spring bean的初始化有關,由此推測此類主要處理方法都來自這兩個介面的實現方法。同時該類也實現了order方法。

好了,二狗子說:有關AnnotationAwareAspectJAutoProxyCreator類的詳細程式碼和執行流程我們後面再講,他有點消化不了了。

重磅福利

關注「 冰河技術 」微信公眾號,後臺回覆 “設計模式” 關鍵字領取《深入淺出Java 23種設計模式》PDF文件。回覆“Java8”關鍵字領取《Java8新特性教程》PDF文件。回覆“限流”關鍵字獲取《億級流量下的分散式限流解決方案》PDF文件,三本PDF均是由冰河原創並整理的超硬核教程,面試必備!!

好了,今天就聊到這兒吧!別忘了點個贊,給個在看和轉發,讓更多的人看到,一起學習,一起進步!!

寫在最後

如果你覺得冰河寫的還不錯,請微信搜尋並關注「 冰河技術 」微信公眾號,跟冰河學習高併發、分散式、微服務、大資料、網際網路和雲原生技術,「 冰河技術 」微信公眾號更新了大量技術專題,每一篇技術文章乾貨滿滿!不少讀者已經通過閱讀「 冰河技術 」微信公眾號文章,吊打面試官,成功跳槽到大廠;也有不少讀者實現了技術上的飛躍,成為公司的技術骨幹!如果你也想像他們一樣提升自己的能力,實現技術能力的飛躍,進大廠,升職加薪,那就關注「 冰河技術 」微信公眾號吧,每天更新超硬核技術乾貨,讓你對如何提升技術能力不再迷茫!

相關文章