在介紹AOP之前,想必很多人都聽說AOP是基於動態代理和反射來實現的,那么在看AOP之前,你需要弄懂什么是動態代理和反射及它們又是如何實現的。
想了解JDK的動態代理及反射的實現和源碼分析,請參見下面三篇文章
那么接下里進入AOP的環節。
AOP即面向切面編程,剛學AOP的時候,單是各種AOP的概念都搞的有點懵,什么切面,切點,通知,織入、連接點、目標對象。。。。AOP的原理都沒看呢,這些詞語的意思就已經讓人不想看了。本文將在實現AOP的時候,講解我理解的這些AOP的術語,對應的AOP的代碼和動作。
本文將先從AOP代碼實現入手,然后分析AOP的底層代碼及其原理。
一、AOP的Demo
如果我們把對象的繼承關系看成縱向關系,就像一棵樹,多個不同類的多個繼承關系就相當於有一排的樹。AOP的好處就在於,你想對這些樹進行相同的操作時,這個時候,不用縱向的為每個樹定義操作方法,你只需要橫向的一刀切,給他們提供個共有的操作方法。
Spring的AOP是支持JDK的動態代理和Cglib的動態代理的。JDK的動態代理是針對接口的,而Cglib是針對類的。本文針對JDK的動態代理。
首先定義一個接口:起名字時候特意給這個接口名,帶上了Interface,這樣后面會更引人注意一些。接口很簡單,里面一個抽象方法eat()
* 動物接口,提供一個eat的抽象方法
* Created by zsqweilai on 17/6/27.
*/
public interface AnimalInterface {
public abstract void eat();
}
實現類:作為一個吃貨,實現類里面當然得打印 chi chi chi。撐死我吧!!!
這個實現類里面,只有一個方法,這個方法就是AOP的切點。雖然切點這個概念本身並不一定是Method,但在Spring中,所有的切點都是Method。我們增強的是方法。
切面類,又稱增強類。因為我們是要用這個類的方法,來給原先的切點方法增強。切面類中,我們要去執行的方法,稱為通知。所謂織入通知,就是將切面類里面的方法,和切點的方法進行聯系。
接下來通過xml配置的方式,在xml文件里面配置AOP。
配置<aop:pointcut>的時候,通過expressi表達式,定義了com.weili.cn這個包下的所有類的所有方法 為切入點。也就是說,這個包下的所有方法,在調用執行的時候,會被Spring增強。具體在這里的增強,就是在執行這些切點方法之前和之后,會分別執行animalEmpty 和 animalFull方法。
最后就是調用的方法了。
我得說明一點,在我們進行spring-aop.xml解析的時候,aop還沒實現呢。在第二行getBean的時候,才真正進行aop。具體的源碼那里 會說明。
緊接着就是Output輸出了。所以,我們可以看到,獲取的bean,確實是增強后的bean。那么就趕緊看看源碼吧。
二、AOP源碼分析
源碼解析這塊,首先就是bean加載。之前也說了,AOP標簽也是自定義標簽,它的解析也和我們之前自定義標簽一樣,走自定義標簽的解析流程。不同的是,AOP調用的是AOP自己的解析器。由於在 Spring源碼解析之二 ------ 自定義標簽的解析和注冊 中已經很詳細的描述了自定義標簽的解析流程,所以這里我們就不再去一一看bean標簽的解析注冊。
所以AOP的源碼分析,我們將從調用類里面的第二行,ctx.getBean("animal")開始。在你調試走到這里的時候,在ctx中可以看到解析和注冊的bean,我們不妨先來看一下。
如下圖,這個是在第一行代碼執行完畢后,ctx的各個屬性。可以在下圖看到,singlentonObjects中,已經存放了代理生成的animal。生層bean的過程在之前的里面已經講的比較清楚了,這里就不再說明。畢竟AOP嘛,我們需要知道,它是如何在我們需要執行的方法前后將我們需要執行的方法執行完成的。
ctx.getBean("animal")獲取完animal bean后,接下來調用eat()方法。這個時候,會進入JdkDynamicAopProxy類的invoke方法。
在這個invoke方法中,先是獲取代理類targetClass,然后根據method和targetClass獲取此方法對應的攔截器執行鏈chain。
這個chain的內容如下。通過名字可以看到,一個是afterAdvice,一個是beforeAdvice。獲取chain后,構造出一個MethodInvoke方法,然后執行proceed方法。
進入proceed方法。currentInterceptorIndex的初始化值為-1.緊接着就如invoke方法。這里的this是我們的eat方法。
在invoke方法里,這里的mi是我們的interface里面的eat方法。然后執行mi的proceed()方法。
這個時候,會繼續回到開始時候的proceed方法。這個時候獲取到的是
interceptorOrInterceptionAdvice,也就是前面攔截器的list里面的第二個,after的那個方法。然后繼續遞歸調用,會到鏈表的最后一個before方法。
最終會調用before里面的方法,
然后回去執行invokeJoinpoint方法,