寫在前面
最近,二狗子入職了新公司,新入職的那幾天確實有點飄。不過慢慢的,他發現他身邊的人各個身懷絕技啊,有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均是由冰河原創並整理的超硬核教程,面試必備!!
好了,今天就聊到這兒吧!別忘了點個贊,給個在看和轉發,讓更多的人看到,一起學習,一起進步!!
寫在最后
如果你覺得冰河寫的還不錯,請微信搜索並關注「 冰河技術 」微信公眾號,跟冰河學習高並發、分布式、微服務、大數據、互聯網和雲原生技術,「 冰河技術 」微信公眾號更新了大量技術專題,每一篇技術文章干貨滿滿!不少讀者已經通過閱讀「 冰河技術 」微信公眾號文章,吊打面試官,成功跳槽到大廠;也有不少讀者實現了技術上的飛躍,成為公司的技術骨干!如果你也想像他們一樣提升自己的能力,實現技術能力的飛躍,進大廠,升職加薪,那就關注「 冰河技術 」微信公眾號吧,每天更新超硬核技術干貨,讓你對如何提升技術能力不再迷茫!