AOP(Aspect Orient Programming),也就是面向切面編程,做爲面向對象編程的一種補充,當前已經成爲一種比較成熟的編程思想,其實AOP問世的時間並不長,甚至在國內的翻譯還不太統一(另有人翻譯爲「面向方面編程」)。AOP和OOP(Object Orient Programming,面向對象編程)互爲補充,OOP將程序分解成各個層次的對象,而AOP則將程序運行過程分解成各個切面。能夠這樣理解:OOP是從靜態角度考慮程序結構,而AOP則從動態角度考慮程序運行過程。php
爲何須要AOPjava
在傳統OOP變成立,以對象爲核心,整個軟件系統由系列相互依賴的對象組成,而這些對象被抽象成一個一個的類,並容許使用類繼承來管理類與類之間從通常到特殊的關係。隨着軟件規模的增大,應用的逐漸升級,慢慢出現了一些OOP很難解決的問題。
shell
咱們能夠經過分析、抽象出一系列具備必定屬性與行爲的對象,並經過這些對象之間的協做來造成一個完整的軟件功能。因爲對象能夠繼承,所以咱們能夠把具備相同功能或相同特性的屬性抽象到一個井井有條的類結構體系中。隨着軟件規模的不斷擴大,專業化分工愈來愈系列,以及OOP應用實踐的不斷增多,隨之也暴露出一些OOP沒法很好解決的問題。
編程
假設系統中有3段徹底類似的代碼,這些代碼一般會採用「複製」、「粘貼」方式來完成,經過這種複製和粘貼完成的代碼在後期將很難維護:想一想一下,若是有一天,這些被複制和粘貼的代碼須要修改,那麼,是否是會修改這3處呢?若是這段代碼被複制和粘貼了100遍呢,1000遍呢,如何維護?大多數人會想到將這段代碼抽取出來,做爲一個公共的方法,在須要使用這段代碼的地方,調用這個方法便可。這樣若是這段代碼須要修改,只須要修改這個公共的方法便可。但實際的狀況是:即便將公共的部分抽取出來了,每一個地方仍是須要去顯式調用這個方法,這可以解決大部分問題。可是對於一些更加特殊的狀況:應用須要將公共的部分與調用的地方完全分離,那又應該如何解決呢?
緩存
由於軟件系統需求變動時很頻繁的事情,假設系統前期設計方法一、二、3時只實現了核心業務,一段時間以後,咱們須要對這些方法都進行事務控制;又過了一段時間,客戶提出這些方法須要進行合法的用戶驗證,只有合法的用戶才能調用這些方法;又過了一段時間,客戶又提出這些方法須要增長日誌記錄;又過了一段時間……面對這種問題,咱們應該怎麼辦呢?是否是每次先定義一個新的方法,而後再去修改方法一、二、3增長調用新的方法的代碼塊呢?這樣作的工做量也不小啊!
安全
咱們但願有一種特殊的方法:咱們只要定義該方法,無需在方法一、二、3中顯式調用它,系統會「自動」調用該方法。
框架
注意:上面的自動被加上了引號,是由於在編程過程當中,沒有所謂的自動的事情,在程序的世界裏,任何事情都是由代碼驅動的。這裏的自動是指,無需開發者關心,由系統來驅動。
eclipse
上面的想法聽起來很神奇,甚至有些不切實際,但實際上是徹底能夠實現的,實現這個需求的技術就是AOP。AOP專門用於處理系統中分佈於各個模塊(不一樣方法)中的交叉關注點的問題,在JavaEE應用中,經常經過AOP來處理一些具備橫切性質的系統級服務,例如事務管理、安全檢查、緩存、對象池等等,AOP已經成爲一種很是經常使用的方案。
工具
使用AspectJ實現AOP性能
AspectJ是一個基於Java語言的AOP框架,提供了強大的AOP功能,其餘不少AOP框架都借鑑或採納其中的一些思想。因爲Spring 3.0的AOP與AspectJ進行了很好的集成,所以掌握AspectJ是學習Spring AOP的基礎。
AspectJ是Java語言的一個AOP實現,其主要包括兩個部分:第一個部分定義瞭如何表達、定義AOP編程中的語法規範,經過這個規範,咱們能夠方便的用AOP來解決Java語言中存在的交叉關注點的問題;另外一個部分是工具部分,包括編譯器、調試工具等。
AspectJ是最先、功能比較強大的AOP實現之一,對嵌套AOP機制都有較好的實現,不少其餘語言的AOP實現,也借鑑或採納了AspectJ中的不少設計。在Java領域,AspectJ中的不少語法結構基本上已成爲AOP領域的標準。
從Spring 2.0開始,Spring AOP已經引入了對AspectJ的支持,並容許直接使用AspectJ進行AOP編程,而Spring自身的AOP API也努力與AspectJ保持一致。所以,學習SpringAOP就必然須要從AspectJ開始,由於它是Java領域最流行的AOP解決方案,咱們甚至能夠直接使用AspectJ進行AOP編程。
AspectJ的下載和安裝(安裝AspectJ以前,請確保系統已經安裝了JDK)。
1. 下載AspectJ的最新穩定版: http://www.eclipse.org/aspectj/downloads.php#stable_release 下載下來後是一個jar包。
2. 打開命令行,cd到該jar包所在的文件夾,運行java -jar aspectj-1.7.4.jar命令,打開AspectJ的安裝界面。第一個界面是歡迎界面,直接next。
3. 第二個界面中,選擇jre的安裝路徑,next。
4. 第三個界面中,選擇AspectJ的安裝路徑,next。由於安裝過程的實質是解壓一個壓縮包,並不須要太多的依賴於系統,所以路徑能夠任意選擇,這裏我選擇和Java安裝在一塊兒。
5. 安裝完成後,按照界面提示,須要配置classpath和PATH,這裏不作介紹。
至此,AspectJ安裝完成。
AspectJ提供了編譯、運行AspectJ的一些工具命令,這些命令放在AspectJ的bin目錄下,而lib路徑下的aspectjrt.jar則是AspectJ的運行時環境,因此咱們須要分別添加這兩個環境變量。
AspectJ的使用入門
成功安裝AspectJ後,在其安裝目錄下有以下結構:
bin:該路徑下存放了AspectJ的經常使用命令,其中ajc最爲經常使用,其做用相似於javac,用於對普通Java類進行編譯時加強
docs:該路徑下存放了AspectJ的使用說明、參考手冊和API等文檔
lib:該路徑下的4個jar文件是AspectJ運行的核心類庫
license和readme文件
這裏要提到的是,一些文檔、AspectJ的入門書籍,一談到使用AspectJ,就認爲必須使用Eclipse工具,彷佛離開了該工具就不能使用AspectJ了。實際上,雖然AspectJ是Eclipse基金組織的開源項目,並且提供了Eclipse的AJDT(AspectJ Development Tools)插件來開發AspectJ應用,但AspectJ絕對無需依賴於Eclipse工具。
AspectJ的用法很簡單,就像咱們使用JDK編譯、運行Java程序同樣。下面經過一個簡單的程序來示範AspectJ的用法:
public class HelloWorld { public void sayHello(){ System.out.println("Hello AspectJ!"); } public static void main(String args[]) { HelloWorld h = new HelloWorld(); h.sayHello(); } }
毫無疑問,結果將輸出"Hello AspectJ!"字符串。假設如今客戶須要在執行sayHello方法前啓動事務,當該方法結束時關閉事務,在傳統編程模式下,咱們必須手動修改sayHello方法——若是改成使用AspectJ,則能夠無需修改上面的sayHello方法。下面咱們定義一個特殊的「類」:
public aspect TxAspect { void around():call(void sayHello()) { System.out.println("Transaction Begin"); proceed(); System.out.println("Transaction End"); } }
可能有人已經發現,上面的類文件中不是使用class、interface或者enum來定義Java類,而是使用aspect——難道Java語言又增長關鍵字了?No!上面的TxAspect根本不是一個Java類,因此aspect也不是Java支持的關鍵字,它只是AspectJ才認識的關鍵字。
上面"void around"中的內容也不是方法,它只是指定當程序執行HelloWorld對象的sayHello方法時,執行這個代碼塊,其中proceed表示調用原來的sayHello方法。正如前面提到的,Java沒法識別TxAspect.java文件中的內容因此咱們須要使用ajc.exe來執行編譯:
ajc HelloWorld.java TxAspect.java
咱們能夠把ajc命令理解爲javac命令,都用於編譯Java程序,區別是ajc命令能夠識別AspectJ的語法。從這個角度看,咱們能夠將ajc命令當成一個加強版的javac命令。
運行該HelloWorld類依然無需任何改變:
java HelloWorld
其結果將是:
從上面的運行結果來看,咱們能夠徹底不對HelloWorld.java文件作修改,也不用對執行HelloWorld的命令作修改,就能夠實現上文中的實現事務管理的需求。上面的「Transaction Begin」和「Transaction End」僅僅是模擬事務的事件,實際開發中,用代碼替換掉這段輸出便可實現事務管理。
若是客戶又提出了爲方法增長日誌的需求,那也很簡單,咱們能夠再定義一個LogAspect類,以下:
//一樣使用aspect做爲「關鍵字」 public aspect LogAspect { //定義一個名爲logPointcut的PointCut,對應於HelloWorld對象的sayHello方法 pointcut logPointcut():execution(void HelloWorld.sayHello()); //在logPointcut以後指定下面的代碼 after():logPointcut() { System.out.println("Log Recoding"); } }
上面的代碼中定義了一個PointCut——logPointcut,等同於執行HelloWorld對象的sayHello方法,並指定在logPointcut以後執行簡單的代碼塊,也就是說,在sayHello方法結束以後執行輸出語句。使用以下命令編譯這幾個java文件:
ajc *.java
再次運行HelloWorld類,將輸出如下結果:
因而可知,經過使用AspectJ提供的AOP支持,咱們能夠爲sayHello方法不斷增長新功能。
實際上,AspectJ容許同時爲多個方法添加新功能,只要咱們定義Pointcut時指定匹配更多的方法便可,下面是一個代碼片斷:
pointcut xxxPointcut:execution(void H*.say*());
上面的程序中的xxxPointcut將能夠匹配全部以H開頭的類,以say開頭的方法名和返回值爲void類型的全部方法。AspectJ甚至容許下面的形式:
pointcut xxxPointcut:execution(* H*.say*());
若是裝有Java反編譯工具,能夠將HelloWorld.class進行反編譯,咱們將發現該HelloWorld.class文件不是由HelloWorld.java文件編譯獲得的,HelloWorld.class裏面增長了不少新的內容——這代表AspectJ在編譯時已經加強了HelloWorld.class的功能,所以AspectJ一般被稱爲編譯時加強的AOP框架。
拓展:與AspectJ相對的還有另一種AOP框架,它們不須要在編譯的時候對目標類進行加強,而是運行時生成目標類的代理類,該代理類要麼實現了目標類實現的相同接口,要麼是目標類的子類。總之,代理類都對目標類進行了加強處理,前者是JDK動態代理的處理策略,後者是CGLIB代理的處理策略。Spring AOP以建立動態代理的方式來生成代理類,底層既可以使用JDK動態代理,也能夠採用CGLIB代理。通常來講,編譯時加強的AOP框架在性能上更有優點——由於運行時動態加強的AOP框架須要每次運行時都進行動態加強。
【未完,待續】