【Spring註解驅動開發】二狗子讓我給他講講@EnableAspectJAutoProxy註解
阿新 • • 發佈:2020-09-03
## 寫在前面
> 最近,二狗子入職了新公司,新入職的那幾天確實有點飄。不過慢慢的,他發現他身邊的人各個身懷絕技啊,有Spring原始碼的貢獻者,有Dubbo原始碼的貢獻者,有MyBatis原始碼的貢獻者,還有研究AI的大佬,個個都是大神級別的人物。二狗子有點慌,想起自己雖然入職了,但是比起其他人確實差點遠啊。怎麼辦呢?先從基礎補起唄,他發現自己對於Spring的理解還不算太深。於是乎,他讓我給他講講Spring的@EnableAspectJAutoProxy註解。
>#
> 好吧,二狗子要請我吃飯啊!關注 **冰河技術** 微信公眾號,後臺回覆“Spring註解”領取工程原始碼。
>#
> 如果文章對你有點幫助,請點個贊,給個在看和轉發,大家的三連是我持續創作的最大動力!
## @EnableAspectJAutoProxy註解
在配置類上新增@EnableAspectJAutoProxy註解,能夠開啟註解版的AOP功能。也就是說,AOP中如果要使註解版的AOP功能起作用,就需要在配置類上新增@EnableAspectJAutoProxy註解。 我們先來看下@EnableAspectJAutoProxy註解的原始碼,如下所示。
```java
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類的原始碼中,如下所示。
```java
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介面的定義,如下所示。
```java
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()方法中設定斷點,如下所示。
![](https://img-blog.csdnimg.cn/20200902215649906.png)
接下來,我們以debug的方法來執行AopTest類的testAop01()方法。執行後程序進入到斷點位置,如下所示。
![](https://img-blog.csdnimg.cn/20200902215701199.png)
可以看到,程式已經暫停在斷點位置,而且在IDEA的左下角顯示了方法的呼叫棧。
在AspectJAutoProxyRegistrar類的registerBeanDefinitions()方法,首先呼叫AopConfigUtils類的registerAspectJAnnotationAutoProxyCreatorIfNecessary()方法來註冊registry。單看registerAspectJAnnotationAutoProxyCreatorIfNecessary()方法也不難理解,字面含義就是:如果需要的話註冊一個AspectJAnnotationAutoProxyCreator。
接下來,我們進入到AopConfigUtils類的registerAspectJAnnotationAutoProxyCreatorIfNecessary()方法中,如下所示。
![](https://img-blog.csdnimg.cn/20200902215713440.png#pic_center)
在AopConfigUtils類的registerAspectJAnnotationAutoProxyCreatorIfNecessary()方法中,直接呼叫了過載的registerAspectJAnnotationAutoProxyCreatorIfNecessary()方法,我們繼續跟程式碼,如下所示。
![](https://img-blog.csdnimg.cn/2020090221572392.png#pic_center)
可以看到在過載的registerAspectJAnnotationAutoProxyCreatorIfNecessary()方法中直接呼叫了registerOrEscalateApcAsRequired()方法。在registerOrEscalateApcAsRequired()方法中,傳入了AnnotationAwareAspectJAutoProxyCreator.class物件。
我們繼續跟進程式碼,如下所示。
![](https://img-blog.csdnimg.cn/2020090221573396.png)
![](https://img-blog.csdnimg.cn/20200902215743204.png)
我們可以看到,在registerOrEscalateApcAsRequired()方法中,接收到的Class物件的型別為:org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator。
在registerOrEscalateApcAsRequired()方法中方法中,首先判斷registry是否包含org.springframework.aop.config.internalAutoProxyCreator型別的bean。如下所示。
![](https://img-blog.csdnimg.cn/20200902215752520.png)
如果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 條件中,我們繼續看程式碼,如下所示。
![](https://img-blog.csdnimg.cn/20200902215803127.png)
這裡,使用RootBeanDefinition來建立一個beanDefinition,並且將org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator的Class物件作為引數傳遞進來。
![](https://img-blog.csdnimg.cn/20200902215814169.png)
我們繼續往下看程式碼,最終AopConfigUtils類的registerOrEscalateApcAsRequired()方法中,會通過registry呼叫registerBeanDefinition()方法註冊元件,如下所示。
![](https://img-blog.csdnimg.cn/20200902215823220.png)
並且註冊的bean的名稱為org.springframework.aop.config.internalAutoProxyCreator。
接下來,我們繼續看AspectJAutoProxyRegistrar類的registerBeanDefinitions()原始碼,如下所示。
![](https://img-blog.csdnimg.cn/2020090221583369.png)
通過AnnotationConfigUtils類的attributesFor方法來獲取@EnableAspectJAutoProxy註解的資訊。接下來,就是判斷proxyTargetClass屬性的值是否為true,如果為true則呼叫AopConfigUtils類的forceAutoProxyCreatorToUseClassProxying()方法;繼續判斷exposeProxy屬性的值是否為true,如果為true則呼叫AopConfigUtils類的forceAutoProxyCreatorToExposeProxy()方法。
**綜上,向Spring的配置類上新增@EnableAspectJAutoProxy註解後,會向IOC容器中註冊AnnotationAwareAspectJAutoProxyCreator。**
接下來,我們來看下AnnotationAwareAspectJAutoProxyCreator類的結構圖。
![](https://img-blog.csdnimg.cn/20200902215842811.png)
我們簡單梳理下AnnotationAwareAspectJAutoProxyCreato類的核心繼承關係,如下所示。
```bash
AnnotationAwareAspectJAutoProxyCreator
--AspectJAwareAdvisorAutoProxyCreator
--AbstractAdvisorAutoProxyCreator
--AbstractAutoProxyCreator
-- ProxyProcessorSupport, SmartInstantiationAwareBeanPostProcessor
```
檢視繼承關係可以發現,此類實現了Aware與BeanPostProcessor介面,這兩個介面都和Spring bean的初始化有關,由此推測此類主要處理方法都來自這兩個介面的實現方法。同時該類也實現了order方法。
好了,二狗子說:有關AnnotationAwareAspectJAutoProxyCreator類的詳細程式碼和執行流程我們後面再講,他有點消化不了了。
## 重磅福利
關注「 **冰河技術** 」微信公眾號,後臺回覆 “**設計模式**” 關鍵字領取《**深入淺出Java 23種設計模式**》PDF文件。回覆“**Java8**”關鍵字領取《**Java8新特性教程**》PDF文件。回覆“**限流**”關鍵字獲取《**億級流量下的分散式限流解決方案**》PDF文件,三本PDF均是由冰河原創並整理的超硬核教程,面試必備!!
**好了,今天就聊到這兒吧!別忘了點個贊,給個在看和轉發,讓更多的人看到,一起學習,一起進步!!**
## 寫在最後
> 如果你覺得冰河寫的還不錯,請微信搜尋並關注「 **冰河技術** 」微信公眾號,跟冰河學習高併發、分散式、微服務、大資料、網際網路和雲原生技術,「 **冰河技術** 」微信公眾號更新了大量技術專題,每一篇技術文章乾貨滿滿!不少讀者已經通過閱讀「 **冰河技術** 」微信公眾號文章,吊打面試官,成功跳槽到大廠;也有不少讀者實現了技術上的飛躍,成為公司的技術骨幹!如果你也想像他們一樣提升自己的能力,實現技術能力的飛躍,進大廠,升職加薪,那就關注「 **冰河技術** 」微信公眾號吧,每天更新超硬核技術乾貨,讓你對如何提升技術能力不再迷茫!
![](https://img-blog.csdnimg.cn/20200716220443647.png#pic_