輕鬆理解AOP(面向切面編程)

本文主要介紹AOP思想,而不是Spring,Spring在本文只做爲理解AOP的工具和例子,所以也不打算介紹Spring的Aspect、Join point、Advice、AOP proxy等概念,那樣初學者會很難理解,如果你懂了AOP的思想,那麼Spring的AOP,還是AspectJ都容易理解了。

Spring如此流行,當我第一次接觸Spring的時候,到網上看了一些文章,都講得神乎其乎,最後我篇也沒看懂,我當時就是認爲這個東西一定很高深,於是我就遇到做WEB開發的人就會打聽一下。得到最多的一個回答就是“Spring是一個框架”,然後我就會問框架是什麼,但都沒有一種說法不讓我感覺玄乎乎的,同時也沒有聽懂,於是,我更感覺Spring很神了,這可能叫做朦朧美。還有一種說法就是“Spring兩個首要的特性就是AOP和IoC”,這種說法讓我感覺Spring簡直神的飛上天了,我都不敢接着問了,再問可能想上天去找Spring了,後來我就懷疑這玩意兒真有那麼高深嗎,強列的好奇心讓我實在hold,決定一定要試一下這個神器,於是就看了一些Step By Step的文章,自己寫了一個HelloWorld,發現這AOP確實是一個比較新穎的思想,也算是打破了常規,是從不同方面思考問題。不過沒有那些童鞋說得那麼神。

本文旨在幫助還沒有理解AOP的童鞋看透弄懂AOP,也歡迎高手批評指正

先說一個Spring是什麼吧,大家都是它是一個框架,但框架這個詞對新手有點抽象,以致於越解釋越模糊,不過它確實是個框架的,但那是從功能的角度來定義的,從本質意義上來講,Spring是一個庫,一個Java庫,所以我個人覺得應該這樣回答Spring是什麼:Spring是一個庫,它的功能是提供了一個軟件框架,這個框架目的是使軟件之間的邏輯更加清晰,配置更靈活,實現這個目的的手段使用AOP和IoC,而AOP和IoC是一種思想,是一種什麼樣的思想呢,等下細說,先說AOP在Java裏是利用反射機制實現(你也可以認爲是動態代理,不過動態代理也是反射機制實現的,所以還是先不要管動態代理,我們這裏化繁爲簡,不讓它干擾咱們對AOP的理解),如何使用AOP呢,很簡單滴,等下介紹。

下面先說AOP是什麼樣的思想,我們一步一步慢慢來,先看一下傳統程序的流程,比如銀行系統會有一個取款流程


我們可以把方框裏的流程合爲一個,另外系統還會有一個查詢餘額流程,我們先把這兩個流程放到一起:


有沒有發現,這個兩者有一個相同的驗證流程,我們先把它們圈起來再說下一步:

有沒有想過可以把這個驗證用戶的代碼是提取出來,不放到主流程裏去呢,這就是AOP的作用了,有了AOP,你寫代碼時不要把這個驗證用戶步驟寫進去,即完全不考慮驗證用戶,你寫完之後,在另我一個地方,寫好驗證用戶的代碼,然後告訴Spring你要把這段代碼加到哪幾個地方,Spring就會幫你加過去,而不要你自己Copy過去,這裏還是兩個地方,如果你有多個控制流呢,這個寫代碼的方法可以大大減少你的時間,不過AOP的目的不是這樣,這只是一個“副作用”,真正目的是,你寫代碼的時候,事先只需考慮主流程,而不用考慮那些不重要的流程,懂C的都知道,良好的風格要求在函數起始處驗證參數,如果在C上可以用AOP,就可以先不管校驗參數的問題,事後使用AOP就可以隔山打牛的給所有函數一次性加入校驗代碼,而你只需要寫一次校驗代碼。不知道C的沒關係,舉一個通用的例子,經常在debug的時候要打log吧,你也可以寫好主要代碼之後,把打log的代碼寫到另一個單獨的地方,然後命令AOP把你的代碼加過去,注意AOP不會把代碼加到源文件裏,但是它會正確的影響最終的機器代碼。

現在大概明白了AOP了嗎,我們來理一下頭緒,上面那個方框像不像個平面,你可以把它當塊板子,這塊板子插入一些控制流程,這塊板子就可以當成是AOP中的一個切面。所以AOP的本質是在一系列縱向的控制流程中,把那些相同的子流程提取成一個橫向的面,這句話應該好理解吧,我們把縱向流程畫成一條直線,然把相同的部分以綠色突出,如下圖左,而AOP相當於把相同的地方連一條橫線,如下圖右,這個圖沒畫好,大家明白意思就行。

    

這個驗證用戶這個子流程就成了一個條線,也可以理解成一個切面,aspect的意思我認爲是方面,你用什麼實物去類比,只要你能理解都可以。這裏的切面只插了兩三個流程,如果其它流程也需要這個子流程,也可以插到其它地方去。

講了這麼多,那到AOP該如何使用呢?我們要寫一個HelloWorld嗎,我看還是算了,關於這種類型的文章,網上已經氾濫成災,我再寫也不一定比人家寫得好,所以,我會在下面貼幾個我認爲寫得不錯的文章鏈接,但我在這裏先介紹一下Spring如何實現AOP的吧。其實也不難理解,Spring的實現是基於函數(或叫方法)的,就是說,你寫好了一個函數後,你還可以在不更改原來的代碼情況,通過Spring在函數前或函數後動態的加入新的代碼。比如你原來的代是這樣的:

void foo() {
    System.out.println("in foo()");
}
然後你想在函數執行前(當成也可以加到執行後,或前後都加,原理是一樣)加一句:
System.out.println("before execute foo()");
你也可以多加幾句,通過Spring,你可以把這些代碼動態的加到函數前面,而不用改變原來的代碼。從而會得到與以下等效的執行碼:
void foo() {
    System.out.println("before execute foo()");
    System.out.pringln("in foo()");
} 
我這樣一說你可能更想親手試試了,可以看看以下這篇文章,寫得很好,我們在此也感謝其作者的辛勤付出。

一個簡單的Spring的AOP例子


參考資料:

Spring AOP Guide:  http://docs.spring.io/spring/docs/2.5.4/reference/aop.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章