springboot的aop自動代理實現分析筆記

目的

aop功能是spring的核心功能之一,本文分析一下作者的設計思路。
之前寫過一些源碼分析,發現寫的太細,代碼太多根本不方便記憶,所以這次簡單的寫一寫,重點是思路。

一、先上結構圖

這個圖左上的核心類AnnotationAwareAspectJAutoProxyCreator是通過註解import進spring容器的一個處理bean,它有多層繼承結構,有兩個核心操作,分別進行了擴展說明。
在這裏插入圖片描述

二、springboot關於aop自動代理的實現

springboot的自動配置類–>AopAutoConfigurationt很簡單,只是引入spring-framework的@EnableAspectJAutoProxy註解,將激活代理功能。重點是import一個BeanDefinitionRegistry,會比較靈活的註冊你的bean到容器中。

三、@EnableAspectJAutoProxy註解

導入的後置處理器中的方法postProcessBeforeInstantiation,是一個框架。
spring容器比如放的是可以用的bean,類比人類是可以爲社會工作的人。那麼所有的人,在出生(Instantiation)前要有做產檢的操作,出生後要餵養的操作,在上學(初始化init)前要學前教育,在上學後要進行招聘操作。Aspectj代理就是在出生前進行產檢操作。
  • postProcessBeforeInstantiation的核心語句有兩句,下面分析:

1.【核心語句一】對所有的aspectj類進行處理。

  • 方法getAdvicesAndAdvisorsForBean由子類完成。

  • 子類中由**[a]找出所有的advisor** 與\ [b]過濾合適的advisor兩部分組成。

1.1 [a]找出所有的advisor

[a]工作由自己aware beanFactory操作時,自己所new的一個類處理的,把beanFactory給它。它也是從容器中找產生advisor的類。
new的這個類,是自己的內部類,局部工作專門它內部類做,如果需要什麼資源找外部類要。但實際中找不到aspectj相關的內容,只是advisor.class的內容。
(這個方法會被子類覆蓋,略過)

[a]工作:當在aspectj情況時,實際上是在子類的子類,即最終的creator類中重寫了一遍(super兼容上面方式的結果),可能是以前沒考慮anotationAspectj的情況吧。同樣new 了一個aspectJAdvisorsBuilder,讓它來生成advisor。不同於父類中只傳了beanFactory,這裏還傳了advisorFactory,也是New出來的一個類。等於是new 出了一個Factory,又new 了一個Builer,把Factory給Builer用。

按說Builer就可以了,爲何又弄一個advisorFactory,而且Factory裏還有一個AspectInstanceFactory?builer中含有兩個緩存,advisorsCache和facotryCache,可能是緩存作用的剝離吧。

ReflectiveAspectJAdvisorFactory是真正的advisorFactory;但aspectj方式要轉成Advisor的,所以有兩個層次的Factory。

爲何不是上層builer自己new一個advisorFactory類呢?advisorFactory內部就New了AspectInstanceFactory。自己用的自己new唄,也許是advisorFactory可以由外部set進去,可修改,統一由外面設置進去。如同我找一個人A做工作,他可以找另一個人B做工作,但另一個人B的工作很重要,我還是自己找好了B介紹給A來用吧。

1.2 類關係特點:

工作讓advisorbuilder做,它又讓advisorFactory來做,但信息不是advisor格式的,是aspectj的,所以它又讓更下一層的AspectInstanceFactory做一部分工作。這麼設計層次明確,產生的類專注於其中重要的部分工作。

1.3 [b]過濾合適的advisor

  • [b]工作也交給AopUtils來處理,參數是上面的結果當前正在後置處理容器中的bean。可看出,這兩個工作都安排其它類專門做。這個操作只是canApply判斷一下,過濾一下,簡單不表。

2.【核心語句二】用上面的處理結果,代理容器中符合條件的類,產生代理類。

2.1 調用過程

  • createProxy工作,由new ProxyFactory()來完成。這裏用了一個我叫做老闆模式的設計。(老闆持有資源信息,對外提供功能調用,但具體工作交內部的類完成,同時把自己傳給下級用來協助工作用的資源)

  • ProxyFactory擁有copyFrom資源,advisors,targetSource等資源與屬性,作爲老闆可以返回最終要的代理對象,是對外功能。

  • 但老闆內部是new 了一個AopProxy具體做事的,就是找了一個真正幹活的DefaultAopProxyFactory來工作的,並把自己傳給它,工作中要啥協助找老闆我。

  • DefaultAopProxyFactory作爲具體幹活的,它有兩個手,比如android和IOS。看情況new出不同的手下來工作,還把老闆傳給它們調用,只告訴它們這是一個協助者。看來這個DefaultAopProxyFactory是中層領導。

  • 最終幹活的,比如是JdkDynamicAopProxy,它要完成代理對象的工作,就必須產生接口,有invokeHandler。它利用老闆的advised信息得到接口,又利用這個信息產生調用鏈,給自己的invoke方法用。

  • invoke時,把所有的調用參數信息(含調用鏈)包裝成一個ReflectiveMethodInvocation,讓它自己推進,給自己最終結果即可。

  • ReflectiveMethodInvocation的推進就是從鏈中找一個調用者,把自己this傳進去。調用者調用後,再返回this推進,再找下一個,再返回自己,再找下一個…真到自己指針記錄鏈條走完了。

2.2 類關係設計特點:

老闆持有參數,找一個人做,這個人按兩個情況找下一級做,下一級用老闆的參數真正工作。而它把老闆的參數進行使用,產生了需要的東東,讓外部的JDK底層來做了。分工合理。
另外想起在recketmq中,有老闆有一個內部類被傳遞到層次功能類使用,功能類需要資源時找這個內部類,內部類天生關聯老闆。類似下派一個祕書。

四、主要的名詞

簡單理解記憶:
advisor:包含一個切點pointcut與對應的advise的組合。aspectj類中的方法都生成一個個advisor。
advise:可以認爲是前置/後置/環繞方法方法攔截器。
advised:advisors與一個接口的組合好。

invocation:包含了調用所有的信息找包,可以讓其自行preceed得到結果
invoke:要外部傳部分內容再invoke,比如method.invoke。
inteceptor攔截時,處理的內容都是外面傳來的。

調用鏈執行一般是:鏈條自己執行時選擇其中一個節點執行同時把自己傳進去,選中的節點執行後,再推動傳來的鏈條再執行,環環相扣。當時鏈條執行完,就執行被代理對象的方法了。鏈條通常是protoType,而節點通常是單例。

五、備註

XML方式配置aop:

5.1 方法一:

<context:component-scan base-package="com.lzj"></context:component-scan>
	<aop:config>
	 	<!-- 配置切入點  expression填寫切入點表達式 --> 
	 	<!-- 切記寫上execution前綴 剛剛就沒寫 啓動報錯!
//execution (* *.*(..) ),這麼寫,就像 【返回值 類.方法(參數)】
// 第一個* 代表着 返回類型是任意的
// 第二個* 代表着 類的全限定名 * 代表着所有
// *(..) 代表着 任意方法 任意的參數
		-->
		<aop:pointcut expression="execution(* com.lzj.service.Impl..*.add(..))" id="pointcut"/>
		<!-- 通知類:配置切面 切面是切入點和通知的結合 -->
		<aop:aspect ref="myAdvise">
			<!-- 指定名爲beforeMethod方法,作爲"前置"通知, -->
			<aop:before method="beforeMethod" pointcut-ref="pointcut"/>
			<aop:around method="around" pointcut-ref="pointcut"/>
			<aop:after-returning method="afterReturn" pointcut-ref="pointcut"/>
			<aop:after-throwing method="afterException" pointcut-ref="pointcut"/>
			<aop:around method="around" pointcut-ref="pointcut"/>
		</aop:aspect>
	</aop:config>
</beans>

<!--還發現配置如下,可以理解一下advisor/adviser-->
<!--還發現配置如下,可以理解一下advisor/adviser-->

        <aop:aspect ref="helloAspect">
            <!—以下使用了兩種方法定義切入點  pointcut-ref和pointcut-->
            <aop:before pointcut-ref="helloPointcut" method="beforeAdvice" />
            <aop:after pointcut="execution(* com.lei.demo.aop.schema..*.*(..))" method="afterFinallyAdvice" />
        </aop:aspect>


    <!-- 配置切面
 advisor:advice(public class HelloAroundAdvice implements MethodInterceptor+ 切點定義。
在一個切點上,做什麼攔截操作的組合,就是一個advisor。所以aspect類的一個前/後,就是一個advisor。
	-->
    <aop:config>
        <aop:pointcut id="helloPointcut" expression="execution(* com.lei.demo.aop.schema..*.*(..))" /><!--advice-ref is a MethodInterceptor-->
        <aop:advisor advice-ref="helloArroundAdvice" pointcut-ref="helloPointcut"/>
    </aop:config>

5.2 方法二

<!--切面類上,寫上切點,方法-->
<beans>
	<context:component-scan base-package="com.lzj"></context:component-scan>
 	<!-- 3.開啓使用註解完成織入 這一步非常重要-->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

5.3 其它aop基礎上的應用

再看其它aop基礎上的應用就比較簡單了,少不了後置處理器,相關的advior與advise的實現不同,都是方法攔截。

異步操作:ProxyAsyncConfiguration

AsyncAnnotationBeanPostProcessor後置處理 aware容器時,產生AsyncAnnotationAdvisor
buildAdvice時,產生AnnotationAsyncExecutionInterceptor
invoke(MethodInvocation)時,產生new Callable執行invocation.proceed()

緩存操作:ProxyCachingConfiguration

advise:BeanFactoryCacheOperationSourceAdvisor Advice:CacheInterceptor
invoke–>execute

事務操作:ProxyTransactionManagementConfiguration

advise:BeanFactoryTransactionAttributeSourceAdvisor
Advice:TransactionInterceptor
invoke–>invokeWithinTransaction

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章