Spring源碼解析之AOP篇

Spring AOP是咱們平常開發中常用的工具,常被用來作統一的日誌、異常處理、監控等功能,使用方法在此很少贅述,有興趣的讀者能夠自行去網上查閱資料進行學習,咱們以註解的使用方式爲例,分析其相關源碼,其餘方式大同小異。html

開啓Spring AOP註解方式首先要配置<aop:aspectj-autoproxy/>標籤,咱們就以這個標籤的解析做爲入口來分析,這裏須要讀者對Spring自定義標籤解析的過程有必定的瞭解,筆者後續也會出相關的文章。鎖定AopNamespaceHandler:api

這裏提到了proxy-target-class和expose-proxy兩個屬性,簡單介紹一下,Spring提供了JDK動態代理和CGLIB代理兩種方式爲目標類建立代理,默認狀況下,若是目標類實現了一個以上的用戶自定義的接口或者目標類自己就是接口,就會使用JDK動態代理,若是目標類自己不是接口而且沒有實現任何接口,就會使用CGLIB代理,若是想強制使用CGLIB代理,則能夠將proxy-target-class設置true,這兩種代理方式在使用的時候有一些須要注意的事項,JDK動態代理是基於實現目標類的接口來建立代理類的,因此只有接口方法會被代理,其餘方法不會被代理,而CGLIB代理是基於繼承目標類實現的,因此不能被繼承的方法(例如final修飾的方法、private修飾的方法等)是不能被代理的,建議儘可能使用JDK動態代理的方式建立代理類。expose-proxy用來解決對象內部this調用沒法被切面加強的問題,例如咱們在A類的對象內部x方法中調用另一個內部方法y時,y方法不會被切面加強,這時能夠配置expose-proxy爲true並將this.y()改成((A)AopContext.currentProxy()).y(),便可讓y方法被切面加強。下面讓咱們來看本篇文章的主角AnnotationAwareAspectJAutoProxyCreator的註冊過程:緩存

咱們發現優先級的判斷就是根據類在APC_PRIORITY_LIST中的索引值來判斷的,索引值越小的優先級越高,咱們看一下APC_PRIORITY_LIST的內容:eclipse

咱們發現它是一個ArrayList,而且在靜態塊中爲其add了三個類,也就是這三個類的優先級依次下降。註冊完AnnotationAwareAspectJAutoProxyCreator以後,要怎麼使用這個bean呢,咱們看一下它的層次結構:ide

咱們發現這個類間接實現了BeanPostProcessor接口,咱們知道,Spring會保證全部bean在實例化的時候都會調用其postProcessAfterInitialization方法,咱們可使用這個方法包裝和改變bean,而真正實現這個方法是在其父類AbstractAutoProxyCreator類中:工具

上面這個方法相信你們已經看出了它的目的,先找出全部對應Advisor的類的beanName,再經過beanFactory.getBean方法獲取這些bean並返回,這裏就是經過父類獲取其餘aop配置信息。下面咱們來看註解aop配置信息的獲取:post

方法很長,不過邏輯很清晰,首先獲取全部bean,而後過濾掉不知足子標籤配置過濾條件的bean,接着判斷bean是否有@Aspect註解,最後解析註解的配置內容並放入緩存中,咱們分部來看:學習

這裏的includePatterns就是文章開始解析<aop:aspectj-autoproxy/>子標籤<aop:include/>的配置時織入的,有興趣的讀者能夠了解一下具體用法,這裏很少贅述。優化

這裏提到了aspect的初始化模式,目前一共有6種,對應PerClauseKind這個枚舉,這裏不作詳細說明,你們能夠到aspect官方文檔進行了解,這裏給出地址:this


https://www.eclipse.org/aspectj/doc/released/aspectj5rt-api/index.html

這裏咱們看到aop相關的一些註解的提取,下面就是初始化過程了:

到這裏整個aop註解方式的初始化工做就完成了,不知道你們是否還記得咱們是怎麼一步一步的走到這裏的,我獲取到了全部的候選加強器,下面要匹配適用於當前bean的加強器:

上面的方法中提到引介加強的概念,在此作簡要說明,引介加強是一種比較特殊的加強類型,它不是在目標方法周圍織入加強,而是爲目標類建立新的方法和屬性,因此引介加強的鏈接點是類級別的,而非方法級別的。經過引介加強,咱們能夠爲目標類添加一個接口的實現,即原來目標類未實現某個接口,經過引介加強能夠爲目標類建立實現該接口的代理,使用方法能夠參考文末的引用連接。另外這個方法用兩個重載的canApply方法爲目標類尋找匹配的加強器,其中第一個canApply方法會調用第二個canApply方法並將第三個參數傳爲false:

咱們來看一下前面初始化的InstantiationModelAwarePointcutAdvisorImpl的層次結構:

咱們看到它實現了PointcutAdvisor接口,因此會調用紅框中的canApply方法進行判斷,第一個參數pca.getPointcut()也就是調用InstantiationModelAwarePointcutAdvisorImpl的getPointcut方法,這個方法的返回值就是咱們看到的在InstantiationModelAwarePointcutAdvisorImpl初始化時傳入的AspectJExpressionPointcut,咱們以AspectJExpressionPointcut做爲第一個參數繼續跟蹤canApply方法:

咱們跟蹤pc.getMethodMatcher()方法也就是AspectJExpressionPointcut的getMethodMatcher方法:

發現方法直接返回this,也就是下面methodMatcher.matches方法就是調用AspectJExpressionPointcut的matches方法:

getShadowMatch方法裏面就是調用aspect提供的api來判斷當前類是否知足execution表達式的規則,有興趣的讀者能夠查閱aspect的相關資料進行學習。在獲取了全部bean匹配的加強器以後,就能夠建立代理了:

這裏咱們看到了Spring若是選擇使用JDK動態代理仍是CGLIB代理,optimize用來控制經過CGLIB建立的代理是否使用激進的優化策略,這個配置對JDK動態代理無效,不推薦使用,proxy-target-class文章開始已經介紹過,邏輯就是在這裏實現的,hasNoUserSuppliedProxyInterfaces判斷目標類是否沒有用戶自定義的代理接口。咱們先看JDK動態代理的方式:

這裏咱們看到爲即將建立的代理類添加了3個接口,後面會用到。JDK動態代理還有一個關鍵的角色就是InvocationHandler,這裏傳入的this,因此咱們判定,JdkDynamicAopProxy必定實現了InvocationHandler接口:

分析其invoke方法:

在DefaultAdvisorAdapterRegistry初始化時初始化了3個適配器,這裏咱們以MethodBeforeAdviceAdapter爲例,也就是對應@Before註解建立的advice的適配器:

下面咱們來看攔截器鏈的調用:

這裏咱們以剛剛適配的MethodBeforeAdviceInterceptor爲例:

這裏首先執行攔截器的before方法,而後再次執行上面的proceed方法進行下一個攔截器方法的調用,這裏的advice也就是獲取候選加強器時生成的AspectJMethodBeforeAdvice:

這裏的aspectJAdviceMethod也就是咱們應用程序中@Before註解的方法了。咱們再來看一個@After註解對應的advice是若是執行的,鎖定AspectJAfterAdvice:

咱們發現是在finally塊中執行了攔截器方法,也就是@After註解的方法會在目標方法執行以後執行。下面咱們來看一下CGLIB代理的方式,這裏須要讀者去了解一下CGLIB以及其建立代理的方式:

這裏將攔截器鏈封裝到了DynamicAdvisedInterceptor中,並加入了Callback,DynamicAdvisedInterceptor實現了CGLIB的MethodInterceptor,因此其核心邏輯在intercept方法中:

這裏咱們看到了與JDK動態代理一樣的獲取攔截器鏈的過程,而且CglibMethodInvokcation繼承了咱們在JDK動態代理看到的ReflectiveMethodInvocation,可是並無重寫其proceed方法,只是重寫了執行目標方法的邏輯,因此總體上是大同小異的。

到這裏,整個Spring 動態AOP的源碼就分析完了,Spring還支持靜態AOP,這裏就不過多贅述了,有興趣的讀者能夠查閱相關資料來學習。

相關文章
相關標籤/搜索