舉個例子來講明一下吧!如今系統中有不少的業務方法,如上傳產品信息、修改產品信息、發佈公司庫等;如今須要對這些方法的執行作性能監控,看每一個業務方法的執行時間;在不改變原業務代碼的基礎上,也許咱們會這麼作:
正則表達式
Offer接口:
編程
Offer實現:
app
Offer代理:
框架
咱們要經過下面的方式來使用:
模塊化
上面的例子的輸出爲:
函數
上面的例子中,OfferProxy實現了IOffer,而全部的業務實現均委託給其成員offer;能夠想像,這應該就是最簡單的AOP的實現了;但這種方式會存在一個問題:若是有很是多的這種業務對象須要性能監控,咱們就須要寫一樣多的XyzProxy來知足需求,這也是很是巨大的工做量。
性能
上面說到了代理,咱們先看看代理模式吧!
學習
下面是代理模式的類圖:
this
代理模式類圖
編碼
代理模式中,存在一個稱爲ProxyObject的代理對象和RealObject的真實對象,它們都實現了相同的接口;在調用的地方持有ProxyObject的實例,當調用request()方法時,ProxyObject能夠在執行RealObject.request()先後作一些特定的業務,甚至不調用RealObject.request()方法。
目前實現代理模式的方式有兩種:基於JDK的動態代理和基於CGLIB字節碼的代理。
2.1 JDK動態代理
JDK動態代理,顧名思義,是基於JDK的反射(reflect)機制;在JDK中,提供了InvocationHandler這個接口,下面是JDK裏面的註釋:
InvocationHandler is the interface implemented by the invocation handler of a proxy instance.
Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.
簡單翻譯,意思是說:該接口由被代理對象的handler所實現;當調用代理對象的方法時,該方法調用將被編碼,而後交給代理對象的invoke方法去執行;
是否是有一種豁然開朗的感受呢?沒錯,答案就在你心中。
這樣,上面的代碼就能夠改爲下面的實現方式:
調用端:
經過這種方式,你不須要爲針對每個業務寫一個代理對象,就能夠很輕鬆地完成你的需求;但也許你已經注意到了,JDK的動態代理,在建立代理對象(上面紅色代碼部分)時,被代理的對象須要實現接口(即面向接口編程);
這就是JDK的動態代理,簡單吧!下面看看CGLIB代理方式。
2.2 CGLIB代理
若是目標對象沒有實現任何接口,那怎麼辦呢?不用擔憂,你能夠用CGLIB來實現代理:
調用端:
使用CGLIB建立的代理對象,其實就是繼承了要代理的目標類,而後對目標類中全部非final方法進行覆蓋,但在覆蓋方法時會添加一些攔截代碼(上面CglibProxyFactory類中的intercept方法)。
下面看看Spring中是如何實現AOP的。
3.1 Spring AOP的幾個概念
Spring AOP中的幾個基本概念,每次學習AOP都被這幾個概念折騰的很不爽,咱們在這裏再把這幾個概念描述一遍,力爭把這幾個概念搞清,在每次review這塊內容的時候能夠很快上手。
用一張圖來形象地表達AOP的概念及其關係以下:
3.2 Spring AOP中切入點、通知、切面的實現
理解了上面的幾個概念後,咱們分別來看看Spring AOP是如何實現這些概念的;
在Pointcut接口的定義中,也許你已經想到了,ClassFilter是類過濾器,它定義了哪些類名須要攔截;典型的兩個實現類爲TypePatternClassFilter和TrueClassFilter(全部類均匹配);而MethodMatcher爲方法匹配器,定義哪些方法須要攔截。
在上面的類圖中:
所謂per-class,即該類型的Advice只提供方法攔截,不會爲目標對象保存任何狀態或者添加新的特性,它也是咱們最多見的Advice。下面是per-class的類圖:
在上面的類圖中,還有兩種類沒有介紹,那就是***AdviceAdapter和***AdviceInteceptor,咱們以AfterReturningAdviceInterceptor爲例來講明:
該類實現了MethodInterceptor和AfterAdvice接口,同時構造函數中還有一個AfterReturningAdvice實例的參數;這個類存在的做用是爲了什麼呢?對,沒錯,Spring AOP把全部的Advice都適配成了MethodInterceptor,統一的好處是方便後面橫切邏輯的執行(參看下一節),適配的工做即由***AdviceAdapter完成;
哈哈,Spring AOP的代碼也不過如此嘛:所謂的AfterReturningAdvice,經過適配成MethodInterceptor後,其實就是在invoke方法中,先執行目標對象的方法,再執行的AfterReturningAdvice所定義的橫切邏輯。你如今明白它爲何不能修改返回值的引用了吧?
對於per-instance的Advice,目前只有一種實現,就是Introduction,使用的場景比較少,有興趣的同窗能夠本身研究一下,呵呵!
接下來看下per-class Advisor的類圖:
其實沒有什麼好看的,前面已經說過,Advisor包含一個Pointcut和一個Advisor;在AbstractGenericPointcutAdvisor中,持有一個Advice的引用;下面的幾個實現,均是針對前面提到的幾種不一樣的Pointcut的實現。
3.3 Spring AOP實現的基本線索
咱們選擇ProxyFactoryBean做爲入口點和分析的開始。ProxyFactoryBean是在Spring IoC環境中,建立AOP應用的最底層方法,從中,能夠看到一條實現AOP的基本線索。
全部的邏輯從如下的方法開始,咱們主要針對單例的代理對象的生成:
下面咱們深刻到SpringAOP核心代碼的內部,看看代理對象的生成機制,攔截器橫切邏輯以及織入的實現。
3.4 代理對象的生成
對於getSingletonInstance()方法返回了什麼,這就是代理對象如何產生的邏輯了,然咱們鬚根溯源,看看傳說中的proxy究竟是如何一步一步的產生的。
ProxyFactoryBean是AdvisedSupport的子類,Spring使用AopProxy接口把AOP代理的實現與框架的其餘部分分離開來。在AdvisedSupport中經過這樣的方式來獲得AopProxy,固然這裏須要獲得AopProxyFactory的幫助 ,從JDK或者cglib中獲得想要的代理對象:
這個DefaultAopProxyFactory是Spring用來生成AopProxy的地方,它包含JDK和Cglib兩種實現方式。讓我接着往裏面看:
能夠看到其中的代理對象能夠由JDK或者Cglib來生成,JdkDynamicAopProxy類和Cglib2AopProxy都實現的是AopProxy的接口,咱們進入JdkDynamicAopProxy實現中看看Proxy是怎樣生成的:
用Proxy包裝target以後,經過ProxyFactoryBean獲得對其方法的調用就被Proxy攔截了, ProxyFactoryBean的getObject()方法獲得的其實是一個Proxy了,target對象已經被封裝了。對 ProxyFactoryBean這個工廠bean而言,其生產出來的對象是封裝了目標對象的代理對象。
3.5 攔截器的做用
前面分析了SpringAOP實現中獲得Proxy對象的過程,接下來咱們去探尋Spring AOP中攔截器鏈是怎樣被調用的,也就是Proxy模式是怎樣起做用的。
還記得在JdkDynamicAopProxy中生成Proxy對象的時候,有一句這樣的代碼嗎?
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
這裏咱們的JdkDynamicAopProxy實現了InvocationHandler這個接口,this參數對應的是InvocationHandler對象,也就是說當 Proxy對象的函數被調用的時候,InvocationHandler的invoke方法會被做爲回調函數調用:
上面所說的目標對象方法的調用,是經過AopUtils的方法調用,使用反射機制來對目標對象的方法進行的:
接下來,咱們來看具體的ReflectiveMethodInvocation中proceed()方法的實現,也就是攔截器鏈的實現機制:
從上面的分析咱們看到了Spring AOP攔截機制的基本實現,好比Spring怎樣獲得Proxy,怎樣利用JAVA Proxy以及反射機制對用戶定義的攔截器鏈進行處理。
3.6 織入的實現
在上面調用攔截器的時候,通過一系列的註冊,適配的過程之後,攔截器在攔截的時候,會調用到預置好的一個通知適配器,設置通知攔截器,這是一系列Spring設計好爲通知服務的類的一個,是最終完成通知攔截和實現的地方,例如對 MethodBeforeAdviceInterceptor的實現是這樣的:
能夠看到通知適配器將advice適配成Interceptor之後,會調用advice的before方法去執行橫切邏輯。這樣就成功的完成了before通知的織入。