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

寫在前面

最近,二狗子入職了新公司,新入職的那幾天確實有點飄。不過慢慢的,他發現他身邊的人各個身懷絕技啊,有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均是由冰河原創並整理的超硬核教程,面試必備!!

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

寫在最後

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

Tags: