目錄
- 前言
- 一、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。

二、AOP 源碼分析從何入手
1.AOP核心類介紹
介紹一些Spring 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(抽象類)
- 容器中查找 Advisor類型(事務)
- 容器中查找注解了@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
附錄
@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 ;
}
}