摘要:,,面向切面編程。,切點,切面匹配連接點的點,一般與切點表達式相關,就是切面如何切點。例子中,注解就是切點表達式,匹配對應的連接點,通知,指在切面的某個特定的連接點上執行的動作。,織入,將作用在的過程。因為源碼都是英文寫的。
之前《零基礎帶你看Spring源碼——IOC控制反轉》詳細講了Spring容器的初始化和加載的原理,后面《你真的完全了解Java動態代理嗎?看這篇就夠了》介紹了下JDK的動態代理。
基于這兩者的實現上,這次來探索下Spring的AOP原理。雖然AOP是基于Spring容器和動態代理,但不了解這兩者原理也絲毫不影響理解AOP的原理實現,因為大家起碼都會用。
AOP,Aspect Oriented Programming,面向切面編程。在很多時候我們寫一些功能的時候,不需要用到繼承這么重的方法,例如對每個方法在執行前打log,在沒有AOP的情況下,我們只能對每個方法都寫一句打log的語句。如果是一個復雜點的功能,那么將會產生許多重復的代碼,而且會對模塊之間有更多的耦合。
然而,在AOP下,我們只需要通過特定的方法,就能直接切入代碼,添加自定義的功能(后續再講AOP里面的概念點)。
下面將從一個簡單的示例入手,拆解示例的內容,通過源碼分析,一步步帶大家讀懂AOP的原理實現。
使用示例以下代碼不以文字形式展示,若需要代碼,可以到github查看完整Demo。
Demo:https://github.com/Zack-Ku/sp...
Spring項目依然是用xml最原始的配置方式,為了只是能簡單地閱讀原理,否則會多很多自動配置的內容在里面。而AOP的配置用的是注解形式,因為畢竟看起來畢竟清晰,容易理解邏輯。
創建一個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()方法結束...
Demo:https://github.com/Zack-Ku/sp...
示例解析與AOP術語概念看到上面的結果,很容易猜想到,LogAspect作用了在TestBean上,使得每次執行TestBean上的方法時,都會執行對應的方法(before/after)。
LogAspect中帶注解@Pointcut的allMethod(),是用來掃描程序中的連接點。當執行一個方法時,命中了連接點,則會根據不同的通知,執行對應的織入代碼。在上面例子中,執行getStr()前會執行LogAspect中的before(),執行getStr()后會執行LogAspect中的after()。
具體的通知包含
@Before,前置通知,執行方法前執行
@AfterReturn,返回通知,正常返回方法后執行
@After,后置通知,方法最終結束后執行,相當于finaly
@Around,環繞通知,圍繞整個方法
@AfterThrowing,異常通知,拋出異常后執行
開發者在命中連接點時,可以通過以上不同的通知,執行對應方法。這就是AOP中的Advisor。
以上的內容其實已經把AOP核心的概念都已經點出來了,我們再深入具體的認識下其中的術語,
Aspect,切面,一個關注點的模塊。
例子中,LogAspect就是切面。
JoinPoint, 連接點,程序執行中的某個點,某個位置。
例子中,testBean.getStr()是連接點。
PointCut,切點,切面匹配連接點的點,一般與切點表達式相關,就是切面如何切點。
例子中,@PointCut注解就是切點表達式,匹配對應的連接點
Advice,通知,指在切面的某個特定的連接點上執行的動作。
例子中,before()與after()方法中的代碼。
TargetObject,目標對象,指被切入的對象。
例子中,從ctx中取出的testBean則是目標對象。
Weave,織入,將Advice作用在JoinPoint的過程。
以上概念看起來可以還比較難懂,可以通過以下一圖(來源于網絡)來理解
請各位讀者和各位程序員,在閱讀源碼的時候,一定要先搞清楚基本概念,和一定一定要知道對應概念的英文,否則在看源碼的時候,根本對不上號,使理解難度大大提高。因為源碼都是英文寫的。
至此AOP的基本使用和概念相信大家都有一定的了解,下面開始從源碼入手,去探索整個Spring AOP的實現。
源碼分析上面的例子之所以能完成AOP的代理,只因為Spring的xml配置里面加了這一句
< aop : aspectj-autoproxy / >
加上了這一個配置,使得整個Spring項目擁有了AOP的功能。全局搜索下aspectj-autoproxy這個字段,可以發現,是這個類AspectJAutoProxyBeanDefinitionParser解析了這個元素。
其中的parse方法調用的是AopNamespaceUtils類中的registerAspectJAnnotationAutoProxyCreatorIfNecessary。這個方法作用是初始化一個AOP專用的Bean,并且注冊到Spring容器中。
解析這三個操作,
第一句,注冊一個AnnotationAwareAspectJAutoProxyCreator(稱它為自動代理器),這個Creator是AOP的操作核心,也是掃描Bean,代理Bean的操作所在。
第二句,解析配置元素,決定代理的模式。其中有JDK動態代理,還有CGLIB代理,這部分后續會再細講。
第三句,作為系統組件,把Creator這個Bean,放到Spring容器中。讓Spring實例化,啟動這個Creator。
自動代理器下面我們來細看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()。
這些入參,對比上一篇講過的動態代理,其實非常相似。
beanClass,加載到Spring,觸發AOP的bean類
targetSource,目標對象,示例中則是從ctx中取出的testBean
specificInterceptors,指定Advisor,示例中則是before和after的方法。
下面來具體看下代理的過程
代碼可以概括為,創建一個proxyFactory對象,然后把上面的參數都丟到這個這個工廠里,最后從proxyFactory獲取一個代理對象。
來看看ProxyFactory的getProxy方法是怎么生成代理對象的。
Debug該方法,可以在DefaultAopProxyFactory中createAopProxy看到
工廠會根據配置與目標對象的類型,選擇用JDK動態代理(參考《你真的完全了解Java動態代理嗎?看這篇就夠了》)還是CGLIB的代理(CGLIB具體在后續講)。
代理后的對象放回ctx中,然后當程序執行的時候,會直接調用這個代理類。
至此整個AOP的代理流程就結束了。下面來了解下CGLIG代理與JDK代理的不同
CGLIB與JDK代理區別CGLIB(Code Generation Library)是一個強大的,高性能,高質量的Code生成類庫。它可以在運行期擴展Java類與實現Java接口。Hibernate支持它來實現PO(Persistent Object 持久化對象)字節碼的動態生成。
回顧下JDK代理,JDK代理需要一組需要實現的接口,然后通過這些接口獲取構造方法,用這個構造方法和InvocationHandler,實例化一個對象出來。所以JDK的方式是基于接口的。
而CGLIB的代理是基于類的,用目標類生成一個子類,子類重寫父類的方法,從而達到動態代理的效果。CGLIB的使用和實現等后面有機會再詳細介紹。目前暫時只要理解兩者不同的使用場景就足夠了。
總結回顧下Spring AOP的流程
Spring加載自動代理器AnnotationAwareAspectJAutoProxyCreator,當作一個系統組件。
當一個bean加載到Spring中時,會觸發自動代理器中的bean后置處理
bean后置處理,會先掃描bean中所有的Advisor
然后用這些Adviosr和其他參數構建ProxyFactory
ProxyFactory會根據配置和目標對象的類型尋找代理的方式(JDK動態代理或CGLIG代理)
然后代理出來的對象放回context中,完成Spring AOP代理
相信大家通過閱讀本文,對Spring的AOP處理有一定的認識。想更深入地了解,探索每一步,每一行代碼的實現,可以下載Demo源碼,一步步地調試
Demo:https://github.com/Zack-Ku/sp...
更多技術文章、精彩干貨,請關注
博客:zackku.com
微信公眾號:Zack說碼
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/77133.html
摘要:問題來了,我們到底還在用嗎答案是,不全用。后者是初始化的配置,主要是的配置。啟動類測試啟動項目后,在瀏覽器里面輸入。通過查詢已裝載的,并且支持該而獲取的。按照前面對的描述,對于而言,這個必定是。的核心在的方法中。 之前已經分析過了Spring的IOC(《零基礎帶你看Spring源碼——IOC控制反轉》)與AOP(《從源碼入手,一文帶你讀懂Spring AOP面向切面編程》)的源碼,本次...
摘要:作為面試官,我是如何甄別應聘者的包裝程度語言和等其他語言的對比分析和主從復制的原理詳解和持久化的原理是什么面試中經常被問到的持久化與恢復實現故障恢復自動化詳解哨兵技術查漏補缺最易錯過的技術要點大掃盲意外宕機不難解決,但你真的懂數據恢復嗎每秒 作為面試官,我是如何甄別應聘者的包裝程度Go語言和Java、python等其他語言的對比分析 Redis和MySQL Redis:主從復制的原理詳...
摘要:作為面試官,我是如何甄別應聘者的包裝程度語言和等其他語言的對比分析和主從復制的原理詳解和持久化的原理是什么面試中經常被問到的持久化與恢復實現故障恢復自動化詳解哨兵技術查漏補缺最易錯過的技術要點大掃盲意外宕機不難解決,但你真的懂數據恢復嗎每秒 作為面試官,我是如何甄別應聘者的包裝程度Go語言和Java、python等其他語言的對比分析 Redis和MySQL Redis:主從復制的原理詳...
摘要:今天整理了一下近大半年以來的一些文章,和我的預期一樣,很多文章我都忘記自己曾經寫過了,這個記錄的過程讓我也有了新的理解。希望大家,收藏,點贊,加轉發。 今天整理了一下近大半年以來的一些文章,和我的預期一樣,很多文章我都忘記自己曾經寫過了,這個記錄的過程讓我也有了新的理解。希望大家,收藏,點贊,加轉發。 面試必備 面試必備:深入Spring MVC DispatchServlet 源碼...
閱讀 526·2023-04-26 00:33
閱讀 3550·2021-11-24 09:39
閱讀 2955·2021-09-22 15:34
閱讀 2328·2019-08-23 18:07
閱讀 2922·2019-08-23 18:04
閱讀 3711·2019-08-23 16:06
閱讀 2903·2019-08-23 15:27
閱讀 1623·2019-08-23 14:32