以前《零基礎帶你看Spring源碼——IOC控制反轉》詳細講了Spring容器的初始化和加載的原理,後面《你真的徹底瞭解Java動態代理嗎?看這篇就夠了》介紹了下JDK的動態代理。java
基於這二者的實現上,此次來探索下Spring的AOP原理。雖然AOP是基於Spring容器和動態代理,但不瞭解這二者原理也絲絕不影響理解AOP的原理實現,由於你們起碼都會用。ios
AOP,Aspect Oriented Programming,面向切面編程。在不少時候咱們寫一些功能的時候,不須要用到繼承這麼重的方法,例如對每一個方法在執行前打log,在沒有AOP的狀況下,咱們只能對每一個方法都寫一句打log的語句。若是是一個複雜點的功能,那麼將會產生許多重複的代碼,並且會對模塊之間有更多的耦合。 然而,在AOP下,咱們只須要經過特定的方法,就能直接切入代碼,添加自定義的功能(後續再講AOP裏面的概念點)。git
下面將從一個簡單的示例入手,拆解示例的內容,經過源碼分析,一步步帶你們讀懂AOP的原理實現。程序員
如下代碼不以文字形式展現,若須要代碼,能夠到github查看完整Demo。
Demo:github.com/Zack-Ku/spr…github
Spring項目依然是用xml最原始的配置方式,爲了只是能簡單地閱讀原理,不然會多不少自動配置的內容在裏面。而AOP的配置用的是註解形式,由於畢竟看起來畢竟清晰,容易理解邏輯。spring
建立一個Gradle項目,添加對應的Spring與AOP的依賴。 (Gradle和Maven相似,都是自動化構建的工具。但與Maven相比,Gradle是基於groovy,採用DSL格式,具備更強的靈活性、簡潔性、拓展性。如今連Spring的官方源碼都是用Gradle的,能夠說是一款面向將來的工具,後續也值得咱們深刻學習。)編程
建立一個Bean,TestBean。網絡
建立AOP的Aspect。工具
而後寫一個啓動類,測試以上配置源碼分析
運行結果:
com.zack.demo.TestBean.getStr()開始執行... getStr():Testing! com.zack.demo.TestBean.getStr()方法結束...
看到上面的結果,很容易猜測到,LogAspect做用了在TestBean上,使得每次執行TestBean上的方法時,都會執行對應的方法(before/after)。
LogAspect中帶註解@Pointcut的allMethod(),是用來掃描程序中的鏈接點。當執行一個方法時,命中了鏈接點,則會根據不一樣的通知,執行對應的織入代碼。在上面例子中,執行getStr()前會執行LogAspect中的before(),執行getStr()後會執行LogAspect中的after()。
具體的通知包含
開發者在命中鏈接點時,能夠經過以上不一樣的通知,執行對應方法。這就是AOP中的Advisor。
以上的內容其實已經把AOP核心的概念都已經點出來了,咱們再深刻具體的認識下其中的術語,
以上概念看起來能夠還比較難懂,能夠經過如下一圖(來源於網絡)來理解
請各位讀者和各位程序員,在閱讀源碼的時候,必定要先搞清楚基本概念,和必定必定要知道對應概念的英文,不然在看源碼的時候,根本對不上號,使理解難度大大提升。由於源碼都是英文寫的。
至此AOP的基本使用和概念相信你們都有必定的瞭解,下面開始從源碼入手,去探索整個Spring AOP的實現。
上面的例子之因此能完成AOP的代理,只由於Spring的xml配置裏面加了這一句
< aop : aspectj-autoproxy / >
加上了這一個配置,使得整個Spring項目擁有了AOP的功能。全局搜索下aspectj-autoproxy這個字段,能夠發現,是這個類AspectJAutoProxyBeanDefinitionParser解析了這個元素。
其中的parse方法調用的是AopNamespaceUtils類中的registerAspectJAnnotationAutoProxyCreatorIfNecessary。這個方法做用是初始化一個AOP專用的Bean,而且註冊到Spring容器中。
解析這三個操做,
下面咱們來細看AnnotationAwareAspectJAutoProxyCreator是怎麼對Bean作AOP的。
AnnotationAwareAspectJAutoProxyCreator的父類AbstractAutoProxyCreator,裏面實現了BeanPostProceesor接口的postProcessAfterInitialization方法(該方法在一個Bean加載到Spring後會執行)。
關聯註釋描述可知,當一個bean加載完後,執行了該方法,會生成一個新的代理對象,返回context中加載。
下面重點看其中的wrapIfNecessary方法。講述了整個AOP的核心流程,是Spring AOP最最最核心的代碼所在。
看到紅框的兩個核心方法,能夠知道,先從剛加載的Bean中掃描出全部的advice和advisor,而後用它來建立一個代理對象。
先看如何掃描出advice和advisor。 一步步Debug getAdvicesAndAdvisorsForBean(),找到BeanFactoryAspectJAdvisorsBuilder中的buildAspectJAdvisors方法。
該方法就是找出Spring容器中存在的AspectBean,而後返回全部AspectBean中的Advisor。
示例中,LogAspect就是AspectBean,而後LogAspect中的before和after方法就是Advisor。 因此最終返回了LogAspect中的Advisor(before和after)。
拿到了全部的Advisor後,就進入了建立代理的流程了createProxy()。
這些入參,對比上一篇講過的動態代理,其實很是類似。
下面來具體看下代理的過程
代碼能夠歸納爲,建立一個proxyFactory對象,而後把上面的參數都丟到這個這個工廠裏,最後從proxyFactory獲取一個代理對象。
來看看ProxyFactory的getProxy方法是怎麼生成代理對象的。
Debug該方法,能夠在DefaultAopProxyFactory中createAopProxy看到
工廠會根據配置與目標對象的類型,選擇用JDK動態代理(參考《你真的徹底瞭解Java動態代理嗎?看這篇就夠了》)仍是CGLIB的代理(CGLIB具體在後續講)。
代理後的對象放回ctx中,而後當程序執行的時候,會直接調用這個代理類。
至此整個AOP的代理流程就結束了。下面來了解下CGLIG代理與JDK代理的不一樣
CGLIB(Code Generation Library)是一個強大的,高性能,高質量的Code生成類庫。它能夠在運行期擴展Java類與實現Java接口。Hibernate支持它來實現PO(Persistent Object 持久化對象)字節碼的動態生成。
回顧下JDK代理,JDK代理須要一組須要實現的接口,而後經過這些接口獲取構造方法,用這個構造方法和InvocationHandler,實例化一個對象出來。因此JDK的方式是基於接口的。
而CGLIB的代理是基於類的,用目標類生成一個子類,子類重寫父類的方法,從而達到動態代理的效果。CGLIB的使用和實現等後面有機會再詳細介紹。目前暫時只要理解二者不一樣的使用場景就足夠了。
回顧下Spring AOP的流程
相信你們經過閱讀本文,對Spring的AOP處理有必定的認識。想更深刻地瞭解,探索每一步,每一行代碼的實現,能夠下載Demo源碼,一步步地調試。
Demo:github.com/Zack-Ku/spr…
做者:Zack 連接:https://juejin.im/post/5b9b1c8be51d450e9942fae4 來源:掘金 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。