AOP 原理解析 跳轉邏輯 代碼演示

AOP 原理解析 跳轉邏輯 代碼演示

建議大家先看“三、總結”,有一個總體認識比較好

視頻總比文章更好理解

一、AOP功能測試

image-20200529222027235

如果都放在了MathCalculator代碼裏,那就是一種耦合的方式

image-20200529222119307

所以定義一個日誌切面類(LogAspects):切面類裏面的方法需要動態感知MathCalculator.div運行到哪裏,然後執行

image-20200529223257175

 通知方法:
* 前置通知(@Before):logStart:在目標方法(div)運行之前運行
* 後置通知(@After):logEnd:在目標方法(div)運行結束之後運行(無論方法正常結束還是異常結束)
* 返回通知(@AfterReturning):logReturn:在目標方法(div)正常返回之後運行
* 異常通知(@AfterThrowing):logException:在目標方法(div)出現異常以後運行

先不用環繞通知,嘗試前面4個。注意不是Junit的@Before,@After

是org.aspectj.lang.annotation.After;

@Before("top.p3wj.aop.MathCalculator.div")

如果想切MathCalculator的所有方法(且不區分參數,加"…"),即MathCalculator.*(…)

1.1

image-20200529224223102

但是看起來非常繁瑣,有公共的切入點,所以可以提取出來。定義一個方法

image-20200529233816416

1.2 加入到容器中

image-20200529225252909

1.3 告訴哪一個類是切面類

image-20200529231426671

1.4 但是最後記住還要開啓AspectJ。給配置類中加 @EnableAspectJAutoProxy 【開啓基於註解的aop模式】

image-20200529230229273

1.5 測試一下是否成功

image-20200529234453223

發現並沒有輸出相應的東西,這是因爲這是我們自己new的,只要容器中的組件纔可以使用切面的功能

1.5.1 帶異常

image-20200529234643728

這下就有了

1.5.2 不帶異常

image-20200529234849476

2.1 那麼怎麼拿到運行時的信息呢?JoinPoint

2.1.1

JoinPoint.getSignature獲得方法簽名

image-20200529235918427

獲得方法名

image-20200529235944269

參數列表

image-20200530000140392

2.1.2

怎麼獲得返回值呢?returning屬性

image-20200530000332367

returning指定誰來封裝這個返回值,比如我們用Object result來接受所有的返回值

image-20200601091041227

2.1.3 獲得異常,通過throwing

也跟returning一樣要指定

image-20200601092640346

不然會報紅色,

image-20200601092728808

這個也一樣,如果不指定,spring不知道這個exception要幹什麼

加上後用1/0看一下結果

image-20200601093132330

那麼沒有這個@AfterThrowing呢?

image-20200601093242622

我們發現是對這個異常沒有進行我們的一個處理的,並沒有輸出那一句話。

2.1.4 JoinPoint位置

image-20200601093603512

我們發現,JoinPotin如果不放在第一個參數,spring是無法解析的

看一下異常:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.context.event.internalEventListenerProcessor': Initialization of bean failed; nested exception is java.lang.IllegalArgumentException: error at ::0 formal unbound in pointcut 

放在第一位就正常了

image-20200601095034789

image-20200601095227532

二、AOP原理

2.1- @EnableAspectJAutoProxy

AOP就是從@EnableAspectJAutoProxy開始的,加了就有AOP,不加就沒有

image-20200601095551805

我們點進去看一下

image-20200601095644226

發現有ImportBeanDefinitionRegistrar,這是之前學的可以給容器bean中自定義註冊

image-20200601095738427

英文不好,我就用Translation插件翻譯了,大家可以在plugins中搜索安裝

可以自定義地來註冊,給BeanDefinitionRegistry

我們回到以前看看以前怎麼做的,回到MainConfig2

image-20200601103527154

ImportSelector

image-20200601103554549

ImportBeanDefinitionRegistrar

image-20200601103627304

image-20200601103704464

那麼AspectJAutoProxyRegistrar註冊了什麼bean呢?我們打一個斷點debug一下
在這裏插入圖片描述

image-20200601104144543

註冊這個組件,如果需要的情況下

image-20200601104347510

調用另一個方法,並傳了AnnotationAwareAspectJAutoProxyCreator.class,我們step into進去瞧瞧

image-20200601104610923

3.1 判斷是否容器中包含org.springframework.aop.config.internalAutoProxyCreator

3.2 包含則取得這個org.springframework.aop.config.internalAutoProxyCreator

3.3判斷這個名字是否等於

image-20200601104811458

對應的他想註冊這個

image-20200601104852093

AnnotationAwareAspectJAutoProxyCreator

他這個是判斷有了就:

image-20200601105132380

但是我們沒有。。。第一次

所以進行了else

image-20200601105150595

3.4

定義一個bean

image-20200601105307014

3.5

image-20200601105350821

然後這個bean的名就叫org.springframework.aop.config.internalAutoProxyCreator

4.1好了,這個beanDefinition就返回了,我們進行下一步

image-20200601105642399

把@EnableAspectJAutoProxy這個註解的信息拿來

4.2 拿來看這個proxyTargetClass,exposeProxy屬性是否爲true

image-20200601105853715

4.3 這個後來再說

如果爲true,就做一些什麼操作。

那麼,重點就在於他給容器註冊了一個AnnotationAwareAspectJAutoProxyCreator,把這個的功能研究出來了,那麼AOP功能就出來了。以後也一樣的,看見有@EnableXX的註解,就再去看看他給容器註冊了什麼組件,再去看這些組件的功能是什麼

image-20200601110455241

5.那我們就來看一下AnnotationAwareAspectJAutoProxyCreator

5.1 它有很多的繼承關係,大家可以放大看一下

AnnotationAwareAspectJAutoProxyCreator

image-20200601110940446

注意XXBeanPostProcessor,bean的後置處理器

BeanFactoryAware,能把工廠傳進來的

image-20200601111108286

2.2- AnnotationAwareAspectJAutoProxy

只要分析清楚作爲後置處理器和BeanFactory做了哪些工作,整個aop的流暢我們就清楚了

因爲是從AbstractAutoProxyCreator開始實現SmartInstantiationAwareBeanPostProcessorBeanFactoryAware接口的

我們點進去看一下

image-20200601112854708

我們發現是它進行setBeanFactory的,我們把斷點打在這

image-20200601113005975

只要是postProcessXX亂七八糟的,都是跟後置處理器有關的

image-20200601113426892

我們把所有跟後置處理器有關的邏輯都打上斷點

直接返回,或返回空方法的我們就不管了

image-20200601113228756

打上斷點:

public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException

image-20200601122232476

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

image-20200601122324655

注意有的方法名字有點像,

image-20200601122409855

一個是postProcessBeforeInstantiation,一個是postProcessBeforeInitialization

一些不是重寫的,自己定義的我們就不打斷點了,例如

image-20200601122602218

我們繼續上一層:
image-20200601122927278

我們發現,在AbstractAdvisorAutoProxyCreator中,

image-20200601123031587

又把setBeanFactory重寫了

image-20200601123249073

它會在setBeanFactory裏邊init一個BeanFactory

image-20200601123332094

我們再看看有沒有跟後置處理器有關的,額。。。。沒有。

4.我們再繼續上面一層,

image-20200601163600563

前面是抽象的,這個是專門針對呀AspectJ的

我們發現,。。。。這個類不是跟beanpost這些有關的,我們把它跳過

5.我們繼續上一層

AnnotationAwareAspectJAutoProxyCreator

image-20200601164247986

image-20200601164054126

我們發現,它重寫了它父親的父親的initBeanFactory

相當於還得調用它,其他的就沒有跟後置處理器有關的了。我們再給他打上斷點

image-20200601164341022

再給他父類的setBeanFactory打上斷點(它爺爺)

image-20200601164503945

2.3- 註冊AnnotationAwareAspectJAutoProxy

通過前面的分析打上了斷點,我們再在這打上斷點

image-20200601164628529

我們來啓動debug一下

image-20200601165006499

啓動,先來到了setBeanFactory

那麼怎麼來的呢?

image-20200601165052253

我們從這裏開始看

1.創建IOC容器

image-20200601165132511

調用一個有參的構造器,就是下面這個,它分爲三步

image-20200601165246027

a)首先無參構造器創建對象。

b)再把我們這個配置類註冊進來

image-20200601165436945

c)調用refresh

refresh之前的文章有介紹,刷新容器的作用,即把容器中的bean全部創建出來

image-20200601165708541

2.這個bean註冊攔截器邏輯是怎樣的呢?(其實之前spring-annotation文章有講過)

image-20200601170057869

3.再往前走

image-20200601170217811

我們來看看這個大方法是什麼

image-20200601170259348

在beanFactory容器中拿到我們所有要創建的後置處理器。爲什麼我們已經定義了呢?因爲我們那個test文件中傳入了配置類,然後配置類裏邊有一個**@EnableAspectJAutoProxy**

image-20200601170507034

image-20200601170523781

之前說過這個註解會爲我們容器注入一個AnnotationAwareAspectJAutoProxyCreator,包括容器中某人的一些後置處理器的定義。來到這一步的時候,只是有這麼一些定義,還沒有創建對象

4.我們繼續,看看有哪些後置處理器

image-20200601172126436

發現有internalAutoProxyCreator

但當時註冊的也是beanDefinetion,即當時註冊的是bean的定義信息

bean的定義信息的類型就是AnnotationAwareAspectJAutoProxyCreator。這個之前有講

image-20200601204421466

可發現他還給beanFactory中加了其他的一些BeanPostProcessor

image-20200601204759012

image-20200601204838447

拿到這些所有的beanProcessor。它來看是不是這個PriorityOrdered下的接口

image-20200601205040832

它是有一個優先級排序,即這些beanPostProcessor首先這個接口來判斷優先級,誰在前,誰在後

image-20200601205216131

5.排序

image-20200601205451006

image-20200601205545331

普通的就是沒有實現優先級接口的

那麼所謂的註冊是什麼呢?

image-20200601210452243

我們現在要註冊這個

它其實就是它,因爲之前的bean定義

image-20200601210558489

我們來看一下它

image-20200601210558490

注意在左上方,是實現了Ordered接口的

image-20200601210911491

它拿到這個名字,然後從beanFactory中獲取

就調到了這裏

image-20200601211100325

又調用doGetBean

image-20200601211227068

這個doGetBean有點長,只截取了一部分

因爲是第一次,容器中肯定沒有

image-20200601211605142

image-20200601212849824

然後又回到了AbstractBeanFactory

image-20200601211826775

image-20200601211857249

現在就是來創建bean

7.那麼我們就來看一下

image-20200601213056575

如何創建名字叫它的beanPostProcessor【AnnotationAwareAspectJAutoProxyCreator】

image-20200602091327443

image-20200602091357784

image-20200602091538538

可以看到這個bean是有的。

這個bean的創建在上面,可以來看一下

image-20200602091918049

我們來看下創建的是什麼

image-20200602091953552

image-20200602092016208

可以看見創建的是名爲internalAutoProxyCreator的,類型爲AnnotationAwareAspectJAutoProxyCreator的bean

image-20200602092326769

賦值是什麼鬼?進去看一下

image-20200602092434131

就是bean的一些屬性,name啊這些,下面我們依次點進去看一下。可能有點長。

image-20200602093318908

我們看見有一個BeanDefinitionHolder

image-20200602093426622

裏面有一個BeanDefinition

image-20200602093659428

我們看見,就是這些屬性

所以spring的對象創建是將Bean的定義信息存儲到這個BeanDefinition相應的屬性中,後面對Bean的操作就直接對BeanDefinition進行,例如拿到這個BeanDefinition後,可以根據裏面的類名、構造函數、構造函數參數,使用反射進行對象initializeBean。

10.這時候創建好了,該初始化了,這裏要注意,我們的beanPostProcessor就是在初始化的前後進行工作的

image-20200602094241694

這裏面有個這個,其實在spring-annotation那篇博客中有涉及到

image-20200602094438247

看是不是這些Aware接口的,是就調用這些方法——Aware接口的方法回調

因爲我們這個類型 的bean是BeanFactoryAware接口的。怕你忘了,我再貼一下圖

image-20200602094644217

image-20200602094719547

所以進入了這個方法。我們倒回去,看看這個Aware的執行完了會是什麼

image-20200602095018184

applyBeanPostProcessorsBeforeInitialization

在初始化之前應用Bean後處理器

看一看。。。

image-20200602095110943

它是調用所有的後置處理器,調用他們的postProcessBeforeInitialization

所以後置處理器在Aware之後,後置處理器又在初始化之前

我們繼續再來看這個3

image-20200602095326410

invokeInitMethods

這個英文簡單,執行初始化方法。在哪自己@Bean的時候可以指定初始化方法。

我們還是進去看一下吧:

image-20200602100020139

執行完這個3後,又有一個4

image-20200602100204786

初始化後應用Bean後置處理器,再點進去

image-20200602100316462

也是跟之前的後置處理器一樣,一個是Before,一個是After,在初始化的前後。

11.我們還是回到我們的方法棧

之前說它實現了BeanFactoryAware的

image-20200602100532916

image-20200602100600394

最終就來到了我們的AbstractAutoProxyCreator.setBeanFctory()

即:

image-20200602101049725

13.我們又倒回去看看,setBeanFactory執行完後又是怎樣的。step over來到下面這個initBeanFactory

image-20200602101749138

看,調到了AnnotationAwareAspectJAutoProxyCreator

也就是我們要給容器中創建的這個AspectJAutoProxyCreator的這個init方法,

它創建了兩個東西,一個ReflectiveAspectJAdvisorFactory反射的AcpectJ工廠,BeanFactoryAspectJAdvisorsBuilderAdapter通知構建器的適配器。相當於把aspectJAdvisorFactory和beanFactory重新包裝了一下

那麼此時AnnotationAwareAspectJAutoProxyCreator就創建成功,並調用了它的initBeanFactory方法

小結:

image-20200602110043679

2.4- AnnotationAwareAspectJAutoProxy執行時機

上面介紹了AnnotationAwareAspectJAutoProxyCreator就可以攔截到這些創建過程

我們來看一下它作爲後置處理器接下來做了什麼

來到這裏

image-20200602110706471

postProcessBeforeInstantiation

看看BeanPostProcessor呢

image-20200602110747386

postProcessBeforeInitialization

其實在前面有提到兩者名字有一點像,那麼他們的區別是什麼呢?
往上看,我們實現的 後置處理器是

image-20200602110939104

image-20200602110957139

image-20200602111024089

注意一下。

那麼爲什麼來到這呢?

image-20200602111119631

我們也來探究一下。回到之前的這個:

image-20200602111316858

是從之前的棧楨這裏來的

然後是getBean->doGetBean,我們去看一下doGetBean

image-20200602111748656

這個sharedInstance得翻到上面

image-20200602111853837

spring是通過這個機制來保證單實例只被創建一次,所有被創建的bean都會被緩存起來。這裏建議大家看視頻

image-20200602112257063

就到了創建bean的createBean,查看bean的一些定義信息

好,下面是AOP的重點!!!,小夥子們打起精神

image-20200602112641687

希望後置處理器在此能返回一個代理對象:

如果能返回這個bean即!=null,它就直接返回。

如果不能,

image-20200602112821983

這裏有一個doCreateBean,我們點進去

image-20200602113013638

前面內容有了,這裏就不贅述了。和3.6流暢一樣

我們現在是停到resolveBeforeInstantiation了,我們點進去看一下

後置處理器先嚐試返回對象

image-20200602113336903

我們先看看applyBeanPostProcessorsBeforeInstantiation是幹什麼的,點進去

image-20200602113540714

如果說是InstantiationAwareBeanPostProcessor,就調用postProcessBeforeInstantiation方法

區別:BeanPostProcessor是在Bean對象創建完成初始化前後調用

InstantiationAwareBeanPostProcessor書在創建Bean實例之前先嚐試用後置處理器返回對象的

不是以前學的postProcessBeforeInitialization

之前說了,我們的AnnotationAwareAspectJAutoProxyCreator就是InstantiationAwareBeanPostProcessor後置處理器

創建Bean,AnnotationAwareAspectJAutoProxyCreator會在任何bean創建之前先嚐試返回bean的實例。它在所有bean創建之前會進行攔截,因爲它是這個InstantiationAwareBeanPostProcessor後置處理器

image-20200602143306027

小結:

image-20200602143713411

2.5- 創建AOP代理

我們來看看

InstantiationAwareBeanPostProcessor做了什麼,我們來重新debug

 AnnotationAwareAspectJAutoProxyCreator【InstantiationAwareBeanPostProcessor】

image-20200602143846668

現在是創建容器中第一個bean,這是一個循環創建的過程

再放行

image-20200602144219610

再放行,下面就一直方向了

image-20200602144407667

出了點問題,我直接截視頻裏的了。

image-20200602145209906

到了我們的計算器

image-20200602145228503

adviseBeans,已經增強的bean

image-20200602145330724

image-20200602145352529

等等。判斷我們的calculator是否在adviseBeans裏面

image-20200602145647680

判斷是否是基礎的類型,點進去看一下。

image-20200602145723272

即是不是Advice,PointCut,Advisor,AopInfrastructureBean這些接口

點進去看還會判斷是否爲切面

image-20200602145939243

image-20200602145916600

image-20200602150013202

判斷是否有切面這個註解.即實現和註解都算作切面

image-20200602150315932

判斷是否需要跳過,即不處理這個bean

image-20200602150402489

findCandidateAdvisors找到候選的增強器

現在有4個增強器(就是我們切面裏面的通知方法)

image-20200602150526309

第一個:

image-20200602150611155

下一個

image-20200602150630819

下面就展示了,就是4個通知方法

每一個通知方法的增強器是InstantiationModelAwarePointcutAdvisor

image-20200602154719051

最終又調用父類的shouldSkip

接着就到了Initialization

image-20200602155252213

這裏有一個

image-20200602155404736

wrapIfNecessary。包裝,如果需要的情況下

image-20200602155735187

這一節建議先自己看多看幾遍源碼,不然看視頻也跟不走

跟之前一樣,也是判斷是否爲isInfrastructureClass等等

image-20200602155956659

findEligibleAdvisors找到可用的增強器

image-20200602160156699

來找到增強其可以應用到beanClass

找到能在當前bean使用的增強器(找哪些通知方法是需要切入當前bean方法的),怎麼找呢?

image-20200602160827479

用這個AopUtils來找到這個findAdvisorsThatConApply

來看一下:

image-20200602161056746

先整一個能用的一個LinkedList

通過for循環判斷每一個增強器是不是

image-20200602161242503

類型。

image-20200602161315289

下面還有一個for循環

canApply是用來判斷是否能用,怎麼叫能用呢?我們apply進去

image-20200602161433194

用切面表達式算一下每一個方法是否匹配

找到後還做了一個排序:

image-20200602162801774

即調用哪些通知方法都是有順序的,繼續就走到了這:

image-20200602162835062

然後就是這

在這裏插入圖片描述

我們這些已經指定好的攔截器,我們增強器就是要攔截目標方法執行的

20200602163127562
把增強器拿到,然後把當前bean已經增強過了,放到緩存裏面保存一下

即:如果該bean是需要增強的就會來到這一步

image-20200602163327845

image-20200602163403986

這是獲取到的增強器

插入一句image-20200602164512430

直接貼的視頻中的圖是因爲我打斷點進入不了視頻中的方法,原因在於我沒有把它標示爲spring項目,導致我的切面並沒有真正的識別到方法

當你的方法旁邊的提升不是

image-20200602164650081

而是

This advice advises no method就會出現這樣的問題

解決方法:

打開Project Strusture

image-20200602164905189

添加就可以了

這個代理對象怎麼創建呢?

image-20200602173218614

還是拿到這個增強器,保存到代理工程proxyFactory

image-20200602173321010

用我們這個代理工程image-20200602192401258來創建對象

來看看這個AOP怎麼創建的

返回回來後

image-20200602192526842

image-20200602173407429

先來得到AOP代理的創建工廠,然後創建AOP(爲這個this對象)

現在來創建AOP代理

會有兩種形式的代理對象

image-20200602192831361

一種JdkDynamicAopProxy,一種是ObjenesisCglibAopProxy

這是spring自動決定

如果這個類是實現接口的能用jdk代理就用jdk代理。如果沒有實現,例如我們的MathCalculator就用Cglib。我們也可以強制使用Cglib,這個後面再說

會爲我們創建一個代理對象

image-20200602193233863

可以看見是使用Cglib增強的代理

相當於wrap的方法就用完了,給容器返回增強了代理的對象

組件想要執行方法,Cglib就會提槍調用那些我們保存了的通知

以後容器獲取到的就是這個組件的代理對象,執行目標方法的時候,這個代理對象就會這個切面通知方法的流程

image-20200602193703020

image-20200602193724811

在bean的創建前後會做一些事情,會判斷這個對象是否需要包裝,即需要被切入(增強),就會創建代理對象,容器拿到的就是一個代理對象。對象執行方法就是這個代理對象在執行方法。

2.6- 獲得攔截器鏈-MethodInterceptor

來說說目標方法的執行

我們來打上斷點進行debug

image-20200602195042707

image-20200602195101713

確定是Cglib增強過的

image-20200602195203850

裏面還封裝了5個增強器

在這裏插入圖片描述

比如第一個增強方法他是這個AspectAfterThrowingAdvice

相當於容器中放的這個代理對象放了我們通知方法的信息,切入哪個方法 的信息

在這裏插入圖片描述

確認MathCalculator類

來到CglibAopProxy.intercept

image-20200602200811474

攔截目標方法的執行(即讓AOP代理先來攔截一下)

來看看他的邏輯。這裏有一個chain

1.獲取攔截器鏈

image-20200602201117192

根據這個對象獲取攔截器鏈

image-20200602201301273

獲取目標方法的攔截器鏈

image-20200602201417113

如果這個鏈是空的,就methodProxy.invoke。就是沒有攔截器鏈,直接執行目標方法

如果有,它創建一個CglibMethodInvocation對象,然後調用它的proceed方法,

image-20200602202041266

把這些等等都傳入進來

核心就來到了這個攔截器鏈該怎麼獲取,它是來做什麼的。聽起來像是在目標方法執行之前進行攔截的,但是在目標方法執行時我們是要執行通知方法的。所以我們猜測這個攔截器是來告訴通知方法怎麼執行,再來執行目標方法。

image-20200602202502819

我們step into 進來

image-20200602202745406

這些都是緩存,緩存就是保存起來,方便下一次用。

它通過advisorChainFactory的getInterceptorsAndDynamicInterceptionAdvice來獲取攔截器鏈

如果獲取到了,就把這個cached返回。怎麼獲取的呢,我們step into 進去

image-20200602212612436

image-20200602212644850

image-20200602212700159

再返回

所以整個攔截器鏈被封裝在list中

ListinterceptorList保存所有攔截器。注意,list創建的時候已經賦值好了長度

image-20200602221856135

image-20200603101709247

image-20200603101820800

一個是我們默認(ExposeInvocationInterceptor)的,剩下的就是我們的通知方法,

我們繼續往下走

image-20200603103315435

它拿到每一個advisor,判斷如果是一個需要切面切入的增強器

image-20200603105304968

包裝成一個interceptors

image-20200603105413372

再加入進來

image-20200603105454246

如果是其他類型,也一樣的操作.又或者直接傳進來,然後addAll

一句話,遍歷所有的增強器,再將其轉爲interceptor

image-20200603105645957

主要是調用這句話

image-20200603105734260

那麼爲什麼需要轉呢

我們來看一下,第一個advisor是

image-20200603110250477

下來,它是這個PointcutAdvisor

在這裏插入圖片描述

還有一些適配器,做一些轉化再加進來

即如果是MethodInterceptor直接加入到集合中,如果不是(雖然這裏沒有不是的邏輯,待會我們可以debug走一下)就一個for循環,使用增強器的適配器轉爲MethodInterceptor加入到集合中

image-20200603123521489

我們來打一個斷點,並放行

在這裏插入圖片描述

這是一個AspectJAfterThrowingAdvice,我們來看一下這個

image-20200603125832249

發現它是這個MethodInterceptor

image-20200603125936719

然後這3個適配器,我們點擊下一步,

image-20200603130031081

都沒滿足if的條件,即

image-20200603130124289

然後返回。

再來看看第二個:

image-20200603130242908

是這個AspectJAfterreturningAdvicem,它不是MethodInterceptor

image-20200603130411713

直接來到了for

image-20200603130510308

第一個是MethodBeforeAdviceAdapter,專門用來轉前置通知的,它肯定是不支持我們這個afterReturning的,所以

image-20200603130711233

if就沒有成功。

再來看第二個AfterReturningAdviceAdapter

image-20200603130746035

image-20200603130829762

進來了,它是支持的。我們進去這個AfterReturningAdviceAdapter,這個advisorAdapter看看它是怎麼轉的

image-20200603131222692

實際上就是把這個advice拿過來給他包裝一個AfterReturningAdviceInterceptor

image-20200603131344731

即是這個MethodInterceptor就直接放,不是就給包裝過來

第三個後置通知也是直接ok的。

image-20200603131619405

第四個是這個前置通知

image-20200603131643995

前置通知不是,用adapter轉換

我們讓它全部走完,現在全部轉爲了

image-20200603131812000

MethodInterceptor

最終回來,可以知道AfterReturningAdviceInterceptor跟MethodBeforeAdviceInterceptor是用adaptor轉過來的

image-20200603132207212

轉完過後,其實攔截器鏈就出來了,攔截器鏈裏面就是每一個通知方法

image-20200603132433834

接下來往下走

image-20200603134012772

new Cglib,

image-20200603134041904

然後.proceed()來執行我們的攔截器

小結

image-20200603134659132

2.7- 鏈式調用通知方法

下面來探究這個proceed方法

記錄下攔截器鏈中有哪些內容

image-20200603135222611

如果攔截器鏈是空的就創建一個,我們就不看了,直接來到else

image-20200603135400755

image-20200603135552029

之前有講,進去看一眼

image-20200603140121853

所以我們執行的是CglibMethodInvocaltion的proceed

可以看見,這裏有一個索引

image-20200603144824796

image-20200603144901293

它默認是-1,這裏做了一個判斷即如果“-1 == this.interceptorsAndDynamicMethodMatchers.size() - 1”

這個size是什麼呢?
image-20200603145015273

正好是這個攔截器。假如沒有攔截器鏈,就相等了。

我們點進去看一下如果沒有發生了什麼?
image-20200603145129137

image-20200603145211152

其實就爲反射創建。method.invoke()

currentInterceptorIndex記錄當前攔截器的索引,如果當前是第四個攔截器了,當前索引爲4。當前攔截器大小正好是5-1=4

它有兩種情況:
1.如果沒有攔截器,直接執行目標方法,或者執行攔截器的索引和數組大小-1大小一樣(指定到了最後一個攔截器),一會兒來看這個過程。反正currentInterceptorIndex是用來記錄索引

image-20200603155834512

現在索引是-1,攔截器有5個

image-20200603155938280

第0號就是這個ExposeInvocationinterceptor

3.拿到之後怎麼做呢?step into

image-20200603160418639

它會調用invoke方法,然後把(this)傳進來,這個this就是ExposeInvocationInterceptor對象。我們點進去:
4.

image-20200603161019517

它是先從invocation裏面get,點進去看一下invocaiton

image-20200603161208761

它是一個ThreadLocal,TreadLocal就是同一條線程共享數據的,它把這個MethodInvocation共享。

第一次還沒有共享,所以它把這個MethodInvocation先放進去

image-20200603164515065

放進去後,執行Cglib的proceed

爲什麼呢,因爲是這麼過來的,它是Cglibimage-20200603164644716

image-20200603164734893

5.我們繼續

image-20200603164814683

咦,同樣的流程,只是索引從之前的-1變成了0

它又自增一些,索引爲1

image-20200603164919022

image-20200603165027067

image-20200603165115148

它就是這麼個執行的。我們接下來繼續

image-20200603165217874

可以看見它又調用這個invoke(this),所有攔截器都會調用這個invoke(this)

進來,

image-20200603165340735

它又是調這個mi.proceed

mi就是我們的Cglib,調這個又是索引自增一次,又是拿下一個。所以流程是這麼下來的

image-20200603165523024

現在是1了。

image-20200603165545370

自增一個變2

再來看他,

image-20200603165636935

還是調invoke(this),我們step into

image-20200603165730600

那就又是一樣了。無限套娃,後面就不掩飾了

也就是責任鏈模式,你可以點擊查看還有其他模式舉例

通過鏈式的方式鎖定到下一個攔截器

我們來到MethodBeforeAdviceInterceptor

image-20200603170415273

它先調advice.beforem,調用前置通知的通知方法

image-20200603170645520

image-20200603170712335

這個interceptor總算做了一些事了

即這個MethodBeforeAdviceInterceptor在invoke的時候是先來調前置通知

它調完了才又進行之前的下一次

現在攔截器的index跟5-1一樣長了

我這裏直接賦值4了

image-20200603171446331

11.來到了這裏,invokeJoinpotint

image-20200603171548852

這個目標方法一執行完就return了

前置通知調完再調用目標方法

image-20200603182943299

然後這是一個遞歸因爲,MethodBeforeAdviceInterceptor一調用完又返回到AspectAfterAdvice了

image-20200603183417962

不管是否有異常,都來執行通知方法,即我們的後置通知,因爲這是我們後置通知的攔截器,有點像springMVC中的攔截器

如果方法沒有拋異常就來到AfterReturningAdviceInterceptor。只不過我們的方法拋了異常,即1/0,

Returning也不做什麼處理的,就拋給上層AspectAfterThrowingAdvice

image-20200603183728249

所以你看來到了AfterThrowingAdvice

image-20200603183756602

我們來看一下AfterReturningAdviceInterceptor

image-20200603183903442

只有你的proceed執行沒有問題,它纔會執行afterReturning

所以說我們的這個 返回通知 是在方法執行沒有問題才執行

這麼說:
現在在這裏有1/0有錯誤

image-20200603184212344

就拋到了AspectAfterThrowingAdvice,且afterReturning沒有執行

image-20200603184322356

Throwing這裏拿到這個異常

image-20200603184358754

image-20200603184427268

image-20200603184447259

且如果有異常,這個異常就拋出去了。有人處理就處理,沒有就拋給虛擬機

這就是我們的AOP流程:

  1. 先來執行我們的前置通知
  2. 執行我們的目標方法
  3. 在執行後置通知
  4. 如果有異常,執行異常通知
  5. 如果沒有異常,執行返回通知

image-20200603184704703

再看一下我們的代碼

MathCalculator

image-20200603184731945

LogAspects

image-20200603185037975

如果1/1

image-20200603185104068

如果1/0

image-20200603185135896

可以看見有異常AfterReturning就沒了

image-20200603200555705

來看這張圖理解理解

image-20200603200631349

三、總結

這一節原理還是很多,建議大家還是多看看視頻,跟着多debug。下面做一下總結:

我們來看在創建這個容器對象的時候

image-20200603202944993

會調用refresh()刷新容器

image-20200603203026247

之前講過,這裏只是回憶一下

image-20200603203102325

有一步是註冊後置處理器。在這一步會創建後置處理器對象

後面一步就是初始化單實例bean

image-20200603203220302

如果要初始化剩下的單實例bean,那麼我們的

image-20200603203253263

都在這了

組件創建完後就判斷是否需要增強(即創建完後有一個postProcessAfterInitialization):

return wrapIfNessary(bean,beanName,cacheKey)來判斷是否需要包裝(增強)。

是的話就將我們的通知方法包裝成增強器(Advisor),給業務邏輯組件創建一個代理對象(Cglib),如果你有接口,它也可以創建jdk動態代理

而代理對象裏面就有我們的增強器。代理對象創建完後,我們的容器就創建完了。

執行目標方法其實就是代理對象執行目標方法。它是用CglibAopProxy.intercept()這個方法進行攔截。攔截過程:1.得到目標方法的攔截器鏈(也就是以前的增強器包裝成攔截器MethodInterceptor)。2.利用攔截器的鏈式機制,依次進入每一個攔截器進行執行。

3.執行效果我們就有兩套:

正常執行:前置通知-》目標方法-〉後置通知—》返回通知

出現一次:前置通知-》目標方法-〉後置通知—》異常通知

AOP的源碼是spring裏較重要的。大家多走幾遍,來加深理解。以上只是爲大家提供走源代碼的一個思路並做下筆記幫助大家加深記憶。

###再來看一下流程
img-20200603223505557

/**
 * AOP:【動態代理】
 *        指在程序運行期間動態的將某段代碼切入到指定方法指定位置進行運行的編程方式;
 * 
 * 1、導入aop模塊;Spring AOP:(spring-aspects)
 * 2、定義一個業務邏輯類(MathCalculator);在業務邏輯運行的時候將日誌進行打印(方法之前、方法運行結束、方法出現異常,xxx)
 * 3、定義一個日誌切面類(LogAspects):切面類裏面的方法需要動態感知MathCalculator.div運行到哪裏然後執行;
 *        通知方法:
 *           前置通知(@Before):logStart:在目標方法(div)運行之前運行
 *           後置通知(@After):logEnd:在目標方法(div)運行結束之後運行(無論方法正常結束還是異常結束)
 *           返回通知(@AfterReturning):logReturn:在目標方法(div)正常返回之後運行
 *           異常通知(@AfterThrowing):logException:在目標方法(div)出現異常以後運行
 *           環繞通知(@Around):動態代理,手動推進目標方法運行(joinPoint.procced())
 * 4、給切面類的目標方法標註何時何地運行(通知註解);
 * 5、將切面類和業務邏輯類(目標方法所在類)都加入到容器中;
 * 6、必須告訴Spring哪個類是切面類(給切面類上加一個註解:@Aspect)
 * [7]、給配置類中加 @EnableAspectJAutoProxy 【開啓基於註解的aop模式】
 *        在Spring中很多的 @EnableXXX;
 * 
 * 三步:
 *     1)、將業務邏輯組件和切面類都加入到容器中;告訴Spring哪個是切面類(@Aspect)
 *     2)、在切面類上的每一個通知方法上標註通知註解,告訴Spring何時何地運行(切入點表達式)
 *  3)、開啓基於註解的aop模式;@EnableAspectJAutoProxy
 *  
 * AOP原理:【看給容器中註冊了什麼組件,這個組件什麼時候工作,這個組件的功能是什麼?】
 *        @EnableAspectJAutoProxy;
 * 1、@EnableAspectJAutoProxy是什麼?
 *        @Import(AspectJAutoProxyRegistrar.class):給容器中導入AspectJAutoProxyRegistrar
 *           利用AspectJAutoProxyRegistrar自定義給容器中註冊bean;BeanDefinetion
 *           internalAutoProxyCreator=AnnotationAwareAspectJAutoProxyCreator
 * 
 *        給容器中註冊一個AnnotationAwareAspectJAutoProxyCreator;
 * 
 * 2、 AnnotationAwareAspectJAutoProxyCreator:
 *        AnnotationAwareAspectJAutoProxyCreator
 *           ->AspectJAwareAdvisorAutoProxyCreator
 *              ->AbstractAdvisorAutoProxyCreator
 *                 ->AbstractAutoProxyCreator
 *                       implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
 *                    關注後置處理器(在bean初始化完成前後做事情)、自動裝配BeanFactory
 * 
 * AbstractAutoProxyCreator.setBeanFactory()
 * AbstractAutoProxyCreator.有後置處理器的邏輯;
 * 
 * AbstractAdvisorAutoProxyCreator.setBeanFactory()-》initBeanFactory()
 * 
 * AnnotationAwareAspectJAutoProxyCreator.initBeanFactory()
 *
 *
 * 流程:
 *        1)、傳入配置類,創建ioc容器
 *        2)、註冊配置類,調用refresh()刷新容器;
 *        3)、registerBeanPostProcessors(beanFactory);註冊bean的後置處理器來方便攔截bean的創建;
 *           1)、先獲取ioc容器已經定義了的需要創建對象的所有BeanPostProcessor
 *           2)、給容器中加別的BeanPostProcessor
 *           3)、優先註冊實現了PriorityOrdered接口的BeanPostProcessor;
 *           4)、再給容器中註冊實現了Ordered接口的BeanPostProcessor;
 *           5)、註冊沒實現優先級接口的BeanPostProcessor;
 *           6)、註冊BeanPostProcessor,實際上就是創建BeanPostProcessor對象,保存在容器中;
 *              創建internalAutoProxyCreator的BeanPostProcessor【AnnotationAwareAspectJAutoProxyCreator】
 *              1)、創建Bean的實例
 *              2)、populateBean;給bean的各種屬性賦值
 *              3)、initializeBean:初始化bean;
 *                    1)、invokeAwareMethods():處理Aware接口的方法回調
 *                    2)、applyBeanPostProcessorsBeforeInitialization():應用後置處理器的postProcessBeforeInitialization()
 *                    3)、invokeInitMethods();執行自定義的初始化方法
 *                    4)、applyBeanPostProcessorsAfterInitialization();執行後置處理器的postProcessAfterInitialization();
 *              4)、BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)創建成功;--》aspectJAdvisorsBuilder
 *           7)、把BeanPostProcessor註冊到BeanFactory中;
 *              beanFactory.addBeanPostProcessor(postProcessor);
 * =======以上是創建和註冊AnnotationAwareAspectJAutoProxyCreator的過程========
 * 
 *           AnnotationAwareAspectJAutoProxyCreator => InstantiationAwareBeanPostProcessor
 *        4)、finishBeanFactoryInitialization(beanFactory);完成BeanFactory初始化工作;創建剩下的單實例bean
 *           1)、遍歷獲取容器中所有的Bean,依次創建對象getBean(beanName);
 *              getBean->doGetBean()->getSingleton()->
 *           2)、創建bean
 *              【AnnotationAwareAspectJAutoProxyCreator在所有bean創建之前會有一個攔截,InstantiationAwareBeanPostProcessor,會調用postProcessBeforeInstantiation()】
 *              1)、先從緩存中獲取當前bean,如果能獲取到,說明bean是之前被創建過的,直接使用,否則再創建;
 *                 只要創建好的Bean都會被緩存起來
 *              2)、createBean();創建bean;
 *                 AnnotationAwareAspectJAutoProxyCreator 會在任何bean創建之前先嚐試返回bean的實例
 *                 【BeanPostProcessor是在Bean對象創建完成初始化前後調用的】
 *                 【InstantiationAwareBeanPostProcessor是在創建Bean實例之前先嚐試用後置處理器返回對象的】
 *                 1)、resolveBeforeInstantiation(beanName, mbdToUse);解析BeforeInstantiation
 *                    希望後置處理器在此能返回一個代理對象;如果能返回代理對象就使用,如果不能就繼續
 *                    1)、後置處理器先嚐試返回對象;
 *                       bean = applyBeanPostProcessorsBeforeInstantiation():
 *                          拿到所有後置處理器,如果是InstantiationAwareBeanPostProcessor;
 *                          就執行postProcessBeforeInstantiation
 *                       if (bean != null) {
                        bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                     }
 * 
 *                 2)、doCreateBean(beanName, mbdToUse, args);真正的去創建一個bean實例;和3.6流程一樣;
 *                 3)、
 *           
 *        
 * AnnotationAwareAspectJAutoProxyCreator【InstantiationAwareBeanPostProcessor】 的作用:
 * 1)、每一個bean創建之前,調用postProcessBeforeInstantiation();
 *        關心MathCalculator和LogAspect的創建
 *        1)、判斷當前bean是否在advisedBeans中(保存了所有需要增強bean)
 *        2)、判斷當前bean是否是基礎類型的Advice、Pointcut、Advisor、AopInfrastructureBean,
 *           或者是否是切面(@Aspect)
 *        3)、是否需要跳過
 *           1)、獲取候選的增強器(切面裏面的通知方法)【List<Advisor> candidateAdvisors】
 *              每一個封裝的通知方法的增強器是 InstantiationModelAwarePointcutAdvisor;
 *              判斷每一個增強器是否是 AspectJPointcutAdvisor 類型的;返回true
 *           2)、永遠返回false
 * 
 * 2)、創建對象
 * postProcessAfterInitialization;
 *        return wrapIfNecessary(bean, beanName, cacheKey);//包裝如果需要的情況下
 *        1)、獲取當前bean的所有增強器(通知方法)  Object[]  specificInterceptors
 *           1、找到候選的所有的增強器(找哪些通知方法是需要切入當前bean方法的)
 *           2、獲取到能在bean使用的增強器。
 *           3、給增強器排序
 *        2)、保存當前bean在advisedBeans中;
 *        3)、如果當前bean需要增強,創建當前bean的代理對象;
 *           1)、獲取所有增強器(通知方法)
 *           2)、保存到proxyFactory
 *           3)、創建代理對象:Spring自動決定
 *              JdkDynamicAopProxy(config);jdk動態代理;
 *              ObjenesisCglibAopProxy(config);cglib的動態代理;
 *        4)、給容器中返回當前組件使用cglib增強了的代理對象;
 *        5)、以後容器中獲取到的就是這個組件的代理對象,執行目標方法的時候,代理對象就會執行通知方法的流程;
 *        
 *     
 *     3)、目標方法執行  ;
 *        容器中保存了組件的代理對象(cglib增強後的對象),這個對象裏面保存了詳細信息(比如增強器,目標對象,xxx);
 *        1)、CglibAopProxy.intercept();攔截目標方法的執行
 *        2)、根據ProxyFactory對象獲取將要執行的目標方法攔截器鏈;
 *           List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
 *           1)、List<Object> interceptorList保存所有攔截器 5
 *              一個默認的ExposeInvocationInterceptor 和 4個增強器;
 *           2)、遍歷所有的增強器,將其轉爲Interceptor;
 *              registry.getInterceptors(advisor);
 *           3)、將增強器轉爲List<MethodInterceptor>;
 *              如果是MethodInterceptor,直接加入到集合中
 *              如果不是,使用AdvisorAdapter將增強器轉爲MethodInterceptor;
 *              轉換完成返回MethodInterceptor數組;
 * 
 *        3)、如果沒有攔截器鏈,直接執行目標方法;
 *           攔截器鏈(每一個通知方法又被包裝爲方法攔截器,利用MethodInterceptor機制)
 *        4)、如果有攔截器鏈,把需要執行的目標對象,目標方法,
 *           攔截器鏈等信息傳入創建一個 CglibMethodInvocation 對象,
 *           並調用 Object retVal =  mi.proceed();
 *        5)、攔截器鏈的觸發過程;
 *           1)、如果沒有攔截器執行執行目標方法,或者攔截器的索引和攔截器數組-1大小一樣(指定到了最後一個攔截器)執行目標方法;
 *           2)、鏈式獲取每一個攔截器,攔截器執行invoke方法,每一個攔截器等待下一個攔截器執行完成返回以後再來執行;
 *              攔截器鏈的機制,保證通知方法與目標方法的執行順序;
 *        
 *     總結:
 *        1)、  @EnableAspectJAutoProxy 開啓AOP功能
 *        2)、 @EnableAspectJAutoProxy 會給容器中註冊一個組件 AnnotationAwareAspectJAutoProxyCreator
 *        3)、AnnotationAwareAspectJAutoProxyCreator是一個後置處理器;
 *        4)、容器的創建流程:
 *           1)、registerBeanPostProcessors()註冊後置處理器;創建AnnotationAwareAspectJAutoProxyCreator對象
 *           2)、finishBeanFactoryInitialization()初始化剩下的單實例bean
 *              1)、創建業務邏輯組件和切面組件
 *              2)、AnnotationAwareAspectJAutoProxyCreator攔截組件的創建過程
 *              3)、組件創建完之後,判斷組件是否需要增強
 *                 是:切面的通知方法,包裝成增強器(Advisor);給業務邏輯組件創建一個代理對象(cglib);
 *        5)、執行目標方法:
 *           1)、代理對象執行目標方法
 *           2)、CglibAopProxy.intercept();
 *              1)、得到目標方法的攔截器鏈(增強器包裝成攔截器MethodInterceptor)
 *              2)、利用攔截器的鏈式機制,依次進入每一個攔截器進行執行;
 *              3)、效果:
 *                 正常執行:前置通知-》目標方法-》後置通知-》返回通知
 *                 出現異常:前置通知-》目標方法-》後置通知-》異常通知
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章