AOP 原理解析 跳轉邏輯 代碼演示
文章目錄
建議大家先看“三、總結”,有一個總體認識比較好
一、AOP功能測試
如果都放在了MathCalculator代碼裏,那就是一種耦合的方式
所以定義一個日誌切面類(LogAspects):切面類裏面的方法需要動態感知MathCalculator.div運行到哪裏,然後執行
通知方法:
* 前置通知(@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
但是看起來非常繁瑣,有公共的切入點,所以可以提取出來。定義一個方法
1.2 加入到容器中
1.3 告訴哪一個類是切面類
1.4 但是最後記住還要開啓AspectJ。給配置類中加 @EnableAspectJAutoProxy 【開啓基於註解的aop模式】
1.5 測試一下是否成功
發現並沒有輸出相應的東西,這是因爲這是我們自己new的,只要容器中的組件纔可以使用切面的功能
1.5.1 帶異常
這下就有了
1.5.2 不帶異常
2.1 那麼怎麼拿到運行時的信息呢?JoinPoint
2.1.1
JoinPoint.getSignature獲得方法簽名
獲得方法名
參數列表
2.1.2
怎麼獲得返回值呢?returning屬性
returning指定誰來封裝這個返回值,比如我們用Object result來接受所有的返回值
2.1.3 獲得異常,通過throwing
也跟returning一樣要指定
不然會報紅色,
這個也一樣,如果不指定,spring不知道這個exception要幹什麼
加上後用1/0看一下結果
那麼沒有這個@AfterThrowing呢?
我們發現是對這個異常沒有進行我們的一個處理的,並沒有輸出那一句話。
2.1.4 JoinPoint位置
我們發現,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
放在第一位就正常了
二、AOP原理
2.1- @EnableAspectJAutoProxy
AOP就是從@EnableAspectJAutoProxy開始的,加了就有AOP,不加就沒有
我們點進去看一下
發現有ImportBeanDefinitionRegistrar,這是之前學的可以給容器bean中自定義註冊
英文不好,我就用Translation插件翻譯了,大家可以在plugins中搜索安裝
可以自定義地來註冊,給BeanDefinitionRegistry
我們回到以前看看以前怎麼做的,回到MainConfig2
ImportSelector
ImportBeanDefinitionRegistrar
那麼AspectJAutoProxyRegistrar註冊了什麼bean呢?我們打一個斷點debug一下
註冊這個組件,如果需要的情況下
調用另一個方法,並傳了AnnotationAwareAspectJAutoProxyCreator.class,我們step into進去瞧瞧
3.1 判斷是否容器中包含org.springframework.aop.config.internalAutoProxyCreator
3.2 包含則取得這個org.springframework.aop.config.internalAutoProxyCreator
3.3判斷這個名字是否等於
對應的他想註冊這個
AnnotationAwareAspectJAutoProxyCreator
他這個是判斷有了就:
但是我們沒有。。。第一次
所以進行了else
3.4
定義一個bean
3.5
然後這個bean的名就叫org.springframework.aop.config.internalAutoProxyCreator
4.1好了,這個beanDefinition就返回了,我們進行下一步
把@EnableAspectJAutoProxy這個註解的信息拿來
4.2 拿來看這個proxyTargetClass,exposeProxy屬性是否爲true
4.3 這個後來再說
如果爲true,就做一些什麼操作。
那麼,重點就在於他給容器註冊了一個AnnotationAwareAspectJAutoProxyCreator,把這個的功能研究出來了,那麼AOP功能就出來了。以後也一樣的,看見有@EnableXX的註解,就再去看看他給容器註冊了什麼組件,再去看這些組件的功能是什麼
5.那我們就來看一下AnnotationAwareAspectJAutoProxyCreator
5.1 它有很多的繼承關係,大家可以放大看一下
注意XXBeanPostProcessor,bean的後置處理器
BeanFactoryAware,能把工廠傳進來的
2.2- AnnotationAwareAspectJAutoProxy
只要分析清楚作爲後置處理器和BeanFactory做了哪些工作,整個aop的流暢我們就清楚了
因爲是從AbstractAutoProxyCreator開始實現SmartInstantiationAwareBeanPostProcessor和BeanFactoryAware接口的
我們點進去看一下
我們發現是它進行setBeanFactory的,我們把斷點打在這
只要是postProcessXX亂七八糟的,都是跟後置處理器有關的
我們把所有跟後置處理器有關的邏輯都打上斷點
直接返回,或返回空方法的我們就不管了
打上斷點:
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
注意有的方法名字有點像,
一個是postProcessBeforeInstantiation,一個是postProcessBeforeInitialization
一些不是重寫的,自己定義的我們就不打斷點了,例如
我們繼續上一層:
我們發現,在AbstractAdvisorAutoProxyCreator中,
又把setBeanFactory重寫了
它會在setBeanFactory裏邊init一個BeanFactory
我們再看看有沒有跟後置處理器有關的,額。。。。沒有。
4.我們再繼續上面一層,
前面是抽象的,這個是專門針對呀AspectJ的
我們發現,。。。。這個類不是跟beanpost這些有關的,我們把它跳過
5.我們繼續上一層
AnnotationAwareAspectJAutoProxyCreator
我們發現,它重寫了它父親的父親的initBeanFactory
相當於還得調用它,其他的就沒有跟後置處理器有關的了。我們再給他打上斷點
再給他父類的setBeanFactory打上斷點(它爺爺)
2.3- 註冊AnnotationAwareAspectJAutoProxy
通過前面的分析打上了斷點,我們再在這打上斷點
我們來啓動debug一下
啓動,先來到了setBeanFactory
那麼怎麼來的呢?
我們從這裏開始看
1.創建IOC容器
調用一個有參的構造器,就是下面這個,它分爲三步
a)首先無參構造器創建對象。
b)再把我們這個配置類註冊進來
c)調用refresh
refresh之前的文章有介紹,刷新容器的作用,即把容器中的bean全部創建出來
2.這個bean註冊攔截器邏輯是怎樣的呢?(其實之前spring-annotation文章有講過)
3.再往前走
我們來看看這個大方法是什麼
在beanFactory容器中拿到我們所有要創建的後置處理器。爲什麼我們已經定義了呢?因爲我們那個test文件中傳入了配置類,然後配置類裏邊有一個**@EnableAspectJAutoProxy**
之前說過這個註解會爲我們容器注入一個AnnotationAwareAspectJAutoProxyCreator,包括容器中某人的一些後置處理器的定義。來到這一步的時候,只是有這麼一些定義,還沒有創建對象
4.我們繼續,看看有哪些後置處理器
發現有internalAutoProxyCreator
但當時註冊的也是beanDefinetion,即當時註冊的是bean的定義信息
bean的定義信息的類型就是AnnotationAwareAspectJAutoProxyCreator。這個之前有講
可發現他還給beanFactory中加了其他的一些BeanPostProcessor
拿到這些所有的beanProcessor。它來看是不是這個PriorityOrdered下的接口
它是有一個優先級排序,即這些beanPostProcessor首先這個接口來判斷優先級,誰在前,誰在後
5.排序
普通的就是沒有實現優先級接口的
那麼所謂的註冊是什麼呢?
我們現在要註冊這個
它其實就是它,因爲之前的bean定義
我們來看一下它
注意在左上方,是實現了Ordered接口的
它拿到這個名字,然後從beanFactory中獲取
就調到了這裏
又調用doGetBean
這個doGetBean有點長,只截取了一部分
因爲是第一次,容器中肯定沒有
然後又回到了AbstractBeanFactory
現在就是來創建bean
7.那麼我們就來看一下
如何創建名字叫它的beanPostProcessor【AnnotationAwareAspectJAutoProxyCreator】
可以看到這個bean是有的。
這個bean的創建在上面,可以來看一下
我們來看下創建的是什麼
可以看見創建的是名爲internalAutoProxyCreator的,類型爲AnnotationAwareAspectJAutoProxyCreator的bean
賦值是什麼鬼?進去看一下
就是bean的一些屬性,name啊這些,下面我們依次點進去看一下。可能有點長。
我們看見有一個BeanDefinitionHolder
裏面有一個BeanDefinition
我們看見,就是這些屬性
所以spring的對象創建是將Bean的定義信息存儲到這個BeanDefinition相應的屬性中,後面對Bean的操作就直接對BeanDefinition進行,例如拿到這個BeanDefinition後,可以根據裏面的類名、構造函數、構造函數參數,使用反射進行對象initializeBean。
10.這時候創建好了,該初始化了,這裏要注意,我們的beanPostProcessor就是在初始化的前後進行工作的
這裏面有個這個,其實在spring-annotation那篇博客中有涉及到
看是不是這些Aware接口的,是就調用這些方法——Aware接口的方法回調
因爲我們這個類型 的bean是BeanFactoryAware接口的。怕你忘了,我再貼一下圖
所以進入了這個方法。我們倒回去,看看這個Aware的執行完了會是什麼
applyBeanPostProcessorsBeforeInitialization
在初始化之前應用Bean後處理器
看一看。。。
它是調用所有的後置處理器,調用他們的postProcessBeforeInitialization
所以後置處理器在Aware之後,後置處理器又在初始化之前
我們繼續再來看這個3
invokeInitMethods
這個英文簡單,執行初始化方法。在哪自己@Bean的時候可以指定初始化方法。
我們還是進去看一下吧:
執行完這個3後,又有一個4
初始化後應用Bean後置處理器,再點進去
也是跟之前的後置處理器一樣,一個是Before,一個是After,在初始化的前後。
11.我們還是回到我們的方法棧
之前說它實現了BeanFactoryAware的
最終就來到了我們的AbstractAutoProxyCreator.setBeanFctory()
即:
13.我們又倒回去看看,setBeanFactory執行完後又是怎樣的。step over來到下面這個initBeanFactory
看,調到了AnnotationAwareAspectJAutoProxyCreator
也就是我們要給容器中創建的這個AspectJAutoProxyCreator的這個init方法,
它創建了兩個東西,一個ReflectiveAspectJAdvisorFactory反射的AcpectJ工廠,BeanFactoryAspectJAdvisorsBuilderAdapter通知構建器的適配器。相當於把aspectJAdvisorFactory和beanFactory重新包裝了一下
那麼此時AnnotationAwareAspectJAutoProxyCreator就創建成功,並調用了它的initBeanFactory方法
小結:
2.4- AnnotationAwareAspectJAutoProxy執行時機
上面介紹了AnnotationAwareAspectJAutoProxyCreator就可以攔截到這些創建過程
我們來看一下它作爲後置處理器接下來做了什麼
來到這裏
postProcessBeforeInstantiation
看看BeanPostProcessor呢
postProcessBeforeInitialization
其實在前面有提到兩者名字有一點像,那麼他們的區別是什麼呢?
往上看,我們實現的 後置處理器是
注意一下。
那麼爲什麼來到這呢?
我們也來探究一下。回到之前的這個:
是從之前的棧楨這裏來的
然後是getBean->doGetBean,我們去看一下doGetBean
這個sharedInstance得翻到上面
spring是通過這個機制來保證單實例只被創建一次,所有被創建的bean都會被緩存起來。這裏建議大家看視頻
就到了創建bean的createBean,查看bean的一些定義信息
好,下面是AOP的重點!!!,小夥子們打起精神
希望後置處理器在此能返回一個代理對象:
如果能返回這個bean即!=null,它就直接返回。
如果不能,
這裏有一個doCreateBean,我們點進去
前面內容有了,這裏就不贅述了。和3.6流暢一樣
我們現在是停到resolveBeforeInstantiation了,我們點進去看一下
後置處理器先嚐試返回對象
我們先看看applyBeanPostProcessorsBeforeInstantiation是幹什麼的,點進去
如果說是InstantiationAwareBeanPostProcessor,就調用postProcessBeforeInstantiation方法
區別:BeanPostProcessor是在Bean對象創建完成初始化前後調用
InstantiationAwareBeanPostProcessor書在創建Bean實例之前先嚐試用後置處理器返回對象的
不是以前學的postProcessBeforeInitialization
之前說了,我們的AnnotationAwareAspectJAutoProxyCreator就是InstantiationAwareBeanPostProcessor後置處理器
創建Bean,AnnotationAwareAspectJAutoProxyCreator會在任何bean創建之前先嚐試返回bean的實例。它在所有bean創建之前會進行攔截,因爲它是這個InstantiationAwareBeanPostProcessor後置處理器
小結:
2.5- 創建AOP代理
我們來看看
InstantiationAwareBeanPostProcessor做了什麼,我們來重新debug
AnnotationAwareAspectJAutoProxyCreator【InstantiationAwareBeanPostProcessor】
現在是創建容器中第一個bean,這是一個循環創建的過程
再放行
再放行,下面就一直方向了
出了點問題,我直接截視頻裏的了。
到了我們的計算器
adviseBeans,已經增強的bean
等等。判斷我們的calculator是否在adviseBeans裏面
判斷是否是基礎的類型,點進去看一下。
即是不是Advice,PointCut,Advisor,AopInfrastructureBean這些接口
點進去看還會判斷是否爲切面
判斷是否有切面這個註解.即實現和註解都算作切面
判斷是否需要跳過,即不處理這個bean
findCandidateAdvisors找到候選的增強器
現在有4個增強器(就是我們切面裏面的通知方法)
第一個:
下一個
下面就展示了,就是4個通知方法
每一個通知方法的增強器是InstantiationModelAwarePointcutAdvisor
最終又調用父類的shouldSkip
接着就到了Initialization
這裏有一個
wrapIfNecessary。包裝,如果需要的情況下
這一節建議先自己看多看幾遍源碼,不然看視頻也跟不走
跟之前一樣,也是判斷是否爲isInfrastructureClass等等
findEligibleAdvisors找到可用的增強器
來找到增強其可以應用到beanClass
找到能在當前bean使用的增強器(找哪些通知方法是需要切入當前bean方法的),怎麼找呢?
用這個AopUtils來找到這個findAdvisorsThatConApply
來看一下:
先整一個能用的一個LinkedList
通過for循環判斷每一個增強器是不是
類型。
下面還有一個for循環
canApply是用來判斷是否能用,怎麼叫能用呢?我們apply進去
用切面表達式算一下每一個方法是否匹配
找到後還做了一個排序:
即調用哪些通知方法都是有順序的,繼續就走到了這:
然後就是這
我們這些已經指定好的攔截器,我們增強器就是要攔截目標方法執行的
把增強器拿到,然後把當前bean已經增強過了,放到緩存裏面保存一下
即:如果該bean是需要增強的就會來到這一步
這是獲取到的增強器
插入一句
直接貼的視頻中的圖是因爲我打斷點進入不了視頻中的方法,原因在於我沒有把它標示爲spring項目,導致我的切面並沒有真正的識別到方法
當你的方法旁邊的提升不是
而是
This advice advises no method就會出現這樣的問題
解決方法:
打開Project Strusture
添加就可以了
這個代理對象怎麼創建呢?
還是拿到這個增強器,保存到代理工程proxyFactory
用我們這個代理工程來創建對象
來看看這個AOP怎麼創建的
返回回來後
先來得到AOP代理的創建工廠,然後創建AOP(爲這個this對象)
現在來創建AOP代理
會有兩種形式的代理對象
一種JdkDynamicAopProxy,一種是ObjenesisCglibAopProxy
這是spring自動決定
如果這個類是實現接口的能用jdk代理就用jdk代理。如果沒有實現,例如我們的MathCalculator就用Cglib。我們也可以強制使用Cglib,這個後面再說
會爲我們創建一個代理對象
可以看見是使用Cglib增強的代理
相當於wrap的方法就用完了,給容器返回增強了代理的對象
組件想要執行方法,Cglib就會提槍調用那些我們保存了的通知
以後容器獲取到的就是這個組件的代理對象,執行目標方法的時候,這個代理對象就會這個切面通知方法的流程
在bean的創建前後會做一些事情,會判斷這個對象是否需要包裝,即需要被切入(增強),就會創建代理對象,容器拿到的就是一個代理對象。對象執行方法就是這個代理對象在執行方法。
2.6- 獲得攔截器鏈-MethodInterceptor
來說說目標方法的執行
我們來打上斷點進行debug
確定是Cglib增強過的
裏面還封裝了5個增強器
比如第一個增強方法他是這個AspectAfterThrowingAdvice
相當於容器中放的這個代理對象放了我們通知方法的信息,切入哪個方法 的信息
確認MathCalculator類
來到CglibAopProxy.intercept
攔截目標方法的執行(即讓AOP代理先來攔截一下)
來看看他的邏輯。這裏有一個chain
1.獲取攔截器鏈
根據這個對象獲取攔截器鏈
獲取目標方法的攔截器鏈
如果這個鏈是空的,就methodProxy.invoke。就是沒有攔截器鏈,直接執行目標方法
如果有,它創建一個CglibMethodInvocation對象,然後調用它的proceed方法,
把這些等等都傳入進來
核心就來到了這個攔截器鏈該怎麼獲取,它是來做什麼的。聽起來像是在目標方法執行之前進行攔截的,但是在目標方法執行時我們是要執行通知方法的。所以我們猜測這個攔截器是來告訴通知方法怎麼執行,再來執行目標方法。
我們step into 進來
這些都是緩存,緩存就是保存起來,方便下一次用。
它通過advisorChainFactory的getInterceptorsAndDynamicInterceptionAdvice來獲取攔截器鏈
如果獲取到了,就把這個cached返回。怎麼獲取的呢,我們step into 進去
再返回
所以整個攔截器鏈被封裝在list中
ListinterceptorList保存所有攔截器。注意,list創建的時候已經賦值好了長度
一個是我們默認(ExposeInvocationInterceptor)的,剩下的就是我們的通知方法,
我們繼續往下走
它拿到每一個advisor,判斷如果是一個需要切面切入的增強器
包裝成一個interceptors
再加入進來
如果是其他類型,也一樣的操作.又或者直接傳進來,然後addAll
一句話,遍歷所有的增強器,再將其轉爲interceptor
主要是調用這句話
那麼爲什麼需要轉呢
我們來看一下,第一個advisor是
下來,它是這個PointcutAdvisor
還有一些適配器,做一些轉化再加進來
即如果是MethodInterceptor直接加入到集合中,如果不是(雖然這裏沒有不是的邏輯,待會我們可以debug走一下)就一個for循環,使用增強器的適配器轉爲MethodInterceptor加入到集合中
我們來打一個斷點,並放行
這是一個AspectJAfterThrowingAdvice,我們來看一下這個
發現它是這個MethodInterceptor
然後這3個適配器,我們點擊下一步,
都沒滿足if的條件,即
然後返回。
再來看看第二個:
是這個AspectJAfterreturningAdvicem,它不是MethodInterceptor
直接來到了for
第一個是MethodBeforeAdviceAdapter,專門用來轉前置通知的,它肯定是不支持我們這個afterReturning的,所以
if就沒有成功。
再來看第二個AfterReturningAdviceAdapter
進來了,它是支持的。我們進去這個AfterReturningAdviceAdapter,這個advisorAdapter看看它是怎麼轉的
實際上就是把這個advice拿過來給他包裝一個AfterReturningAdviceInterceptor
即是這個MethodInterceptor就直接放,不是就給包裝過來
第三個後置通知也是直接ok的。
第四個是這個前置通知
前置通知不是,用adapter轉換
我們讓它全部走完,現在全部轉爲了
MethodInterceptor
最終回來,可以知道AfterReturningAdviceInterceptor跟MethodBeforeAdviceInterceptor是用adaptor轉過來的
轉完過後,其實攔截器鏈就出來了,攔截器鏈裏面就是每一個通知方法
接下來往下走
new Cglib,
然後.proceed()來執行我們的攔截器
小結
2.7- 鏈式調用通知方法
下面來探究這個proceed方法
記錄下攔截器鏈中有哪些內容
如果攔截器鏈是空的就創建一個,我們就不看了,直接來到else
之前有講,進去看一眼
所以我們執行的是CglibMethodInvocaltion的proceed
可以看見,這裏有一個索引
它默認是-1,這裏做了一個判斷即如果“-1 == this.interceptorsAndDynamicMethodMatchers.size() - 1”
這個size是什麼呢?
正好是這個攔截器。假如沒有攔截器鏈,就相等了。
我們點進去看一下如果沒有發生了什麼?
其實就爲反射創建。method.invoke()
currentInterceptorIndex記錄當前攔截器的索引,如果當前是第四個攔截器了,當前索引爲4。當前攔截器大小正好是5-1=4
它有兩種情況:
1.如果沒有攔截器,直接執行目標方法,或者執行攔截器的索引和數組大小-1大小一樣(指定到了最後一個攔截器),一會兒來看這個過程。反正currentInterceptorIndex是用來記錄索引
現在索引是-1,攔截器有5個
第0號就是這個ExposeInvocationinterceptor
3.拿到之後怎麼做呢?step into
它會調用invoke方法,然後把(this)傳進來,這個this就是ExposeInvocationInterceptor對象。我們點進去:
4.
它是先從invocation裏面get,點進去看一下invocaiton
它是一個ThreadLocal,TreadLocal就是同一條線程共享數據的,它把這個MethodInvocation共享。
第一次還沒有共享,所以它把這個MethodInvocation先放進去
放進去後,執行Cglib的proceed
爲什麼呢,因爲是這麼過來的,它是Cglib
5.我們繼續
咦,同樣的流程,只是索引從之前的-1變成了0
它又自增一些,索引爲1
即:
它就是這麼個執行的。我們接下來繼續
可以看見它又調用這個invoke(this),所有攔截器都會調用這個invoke(this)
進來,
它又是調這個mi.proceed
mi就是我們的Cglib,調這個又是索引自增一次,又是拿下一個。所以流程是這麼下來的
現在是1了。
自增一個變2
再來看他,
還是調invoke(this),我們step into
那就又是一樣了。無限套娃,後面就不掩飾了
通過鏈式的方式鎖定到下一個攔截器
我們來到MethodBeforeAdviceInterceptor
它先調advice.beforem,調用前置通知的通知方法
這個interceptor總算做了一些事了
即這個MethodBeforeAdviceInterceptor在invoke的時候是先來調前置通知
它調完了才又進行之前的下一次
現在攔截器的index跟5-1一樣長了
我這裏直接賦值4了
11.來到了這裏,invokeJoinpotint
這個目標方法一執行完就return了
前置通知調完再調用目標方法
然後這是一個遞歸因爲,MethodBeforeAdviceInterceptor一調用完又返回到AspectAfterAdvice了
不管是否有異常,都來執行通知方法,即我們的後置通知,因爲這是我們後置通知的攔截器,有點像springMVC中的攔截器
如果方法沒有拋異常就來到AfterReturningAdviceInterceptor。只不過我們的方法拋了異常,即1/0,
Returning也不做什麼處理的,就拋給上層AspectAfterThrowingAdvice
所以你看來到了AfterThrowingAdvice
我們來看一下AfterReturningAdviceInterceptor
只有你的proceed執行沒有問題,它纔會執行afterReturning
所以說我們的這個 返回通知 是在方法執行沒有問題才執行
這麼說:
現在在這裏有1/0有錯誤
就拋到了AspectAfterThrowingAdvice,且afterReturning沒有執行
Throwing這裏拿到這個異常
且如果有異常,這個異常就拋出去了。有人處理就處理,沒有就拋給虛擬機
這就是我們的AOP流程:
- 先來執行我們的前置通知
- 執行我們的目標方法
- 在執行後置通知
- 如果有異常,執行異常通知
- 如果沒有異常,執行返回通知
再看一下我們的代碼
MathCalculator
LogAspects
如果1/1
如果1/0
可以看見有異常AfterReturning就沒了
來看這張圖理解理解
三、總結
這一節原理還是很多,建議大家還是多看看視頻,跟着多debug。下面做一下總結:
我們來看在創建這個容器對象的時候
會調用refresh()刷新容器
之前講過,這裏只是回憶一下
有一步是註冊後置處理器。在這一步會創建後置處理器對象
後面一步就是初始化單實例bean
如果要初始化剩下的單實例bean,那麼我們的
都在這了
組件創建完後就判斷是否需要增強(即創建完後有一個postProcessAfterInitialization):
return wrapIfNessary(bean,beanName,cacheKey)來判斷是否需要包裝(增強)。
是的話就將我們的通知方法包裝成增強器(Advisor),給業務邏輯組件創建一個代理對象(Cglib),如果你有接口,它也可以創建jdk動態代理
而代理對象裏面就有我們的增強器。代理對象創建完後,我們的容器就創建完了。
執行目標方法其實就是代理對象執行目標方法。它是用CglibAopProxy.intercept()這個方法進行攔截。攔截過程:1.得到目標方法的攔截器鏈(也就是以前的增強器包裝成攔截器MethodInterceptor)。2.利用攔截器的鏈式機制,依次進入每一個攔截器進行執行。
3.執行效果我們就有兩套:
正常執行:前置通知-》目標方法-〉後置通知—》返回通知
出現一次:前置通知-》目標方法-〉後置通知—》異常通知
AOP的源碼是spring裏較重要的。大家多走幾遍,來加深理解。以上只是爲大家提供走源代碼的一個思路並做下筆記幫助大家加深記憶。
###再來看一下流程
/**
* 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)、效果:
* 正常執行:前置通知-》目標方法-》後置通知-》返回通知
* 出現異常:前置通知-》目標方法-》後置通知-》異常通知