Spring AOP之一 ——AOP的概念

引子
AOP的出現並不是要完全取代OOP,而僅是作爲OOP的有益補充。

AOP的應用場合是受限的,它一般只適合於那些具有橫切邏輯的應用場合:如性能監測、訪問控制、事務管理和日誌記錄(雖然有很多文章用日誌作爲講解AOP的實例,但很多人認爲很難用AOP編寫實用的程序日誌)

AOP的由來
按照軟件重構思想的理念,如果多個類中出現相同的代碼,應該考慮定義一個共同的抽象類,將這些相同的代碼提取到抽象類中。比如Horse、Pig、Camel這些對象都有run()、eat()的方法,通過引入一個包含這兩個方法抽象的Animal父類,Horse、Pig、Camel就可以通過繼承Animal複用到run()和eat()的方法。

通過引入父類消除多個類中重複代碼的方式
在大多情況下是可行的。但世界並非永遠這樣簡單,請看下面論壇管理業務類的代碼
public class ForumService {  
    private TransactionManager transManager;  
    private PerformanceMonitor pmonitor;  
    private TopicDao topicDao;  
    private ForumDao forumDao;  
  
    public void removeTopic(int topicId) {  
        pmonitor.start();//①-1性能監控開始  
        transManager.beginTransaction();//②-1 事務處理開始  
  
        topicDao.removeTopic(topicId); //③-1 業務邏輯  
  
        transManager.commit();//②-1事務處理結束  
        pmonitor.end();//①-2 性能監控結束  
    }  
    public void createForum(Forum forum) {  
        pmonitor.start();//①-1性能監控開始  
        transManager.beginTransaction();//②-1 事務處理開始  
  
        forumDao.create(forum); //③-2 業務邏輯  
  
        transManager.commit();//②-1事務處理結束  
        pmonitor.end();//①-2 性能監控結束  
    }  
    …  
}
①的代碼是方法性能監視代碼,它在方法調用前啓動,在方法調用返回前結束,並在內部記錄性能監視的結果信息。 
而②的代碼是事務開始和事務提交的代碼。我們發現③處的業務代碼淹沒在重複化非業務性的代碼之中,性能監視和事務管理這些非業務性代碼葛藤纏樹般包圍着業務性代碼。
假設我們將ForumService業務類看成一段圓木,將removeTopic()和createForum()方法分別看成圓木的一截,我們會發現性能監視和事務管理的代碼就好像一個年輪,而業務代碼是圓木的樹心,這也正是橫切代碼概念的由來。 

我們無法通過抽象父類的方式消除以上所示的重複性橫切代碼,因爲這些橫切邏輯依附在業務類方法的流程中,它們不能轉移到其他地方去。
AOP獨闢蹊徑通過橫向抽取機制爲這類無法通過縱向繼承體系進行抽象的重複性代碼提供瞭解決方案。對於習慣了縱向抽取的開發者來說,可能不容易理解橫向抽取方法的工作機制,因爲Java語言本身不直接提供這種橫向抽象的能力,我們暫把具體實現放在一旁,先通過圖解的方式歸納出AOP的解決思路,

從圖6-2中,我們可以看出AOP希望將這些分散在各個業務邏輯代碼中的相同代碼,通過橫向切割的方式抽取到一個獨立的模塊中,還業務邏輯類一個清新的世界。 


當然,我們知道將這些重複性的橫切邏輯獨立出來是很容易的,但如何將這些獨立的邏輯融合到業務邏輯中完成和原來一樣的業務操作,這纔是事情的關鍵,也正是AOP要解決的主要問題。 


AOP術語
  • 連接點(JoinPoint)
    程序執行的某個特定位置:如類開始初始化前、類初始化後、類某個方法調用前、調用後、方法拋出異常後。
    一個類或一段程序代碼擁有一些具有邊界性質的特定點,這些代碼中的特定點就稱爲“連接點”。Spring僅支持方法的連接點,即僅能在方法調用前、方法調用後、方法拋出異常時以及方法調用前後這些程序執行點織入增強。
    連接點由兩個信息確定:第一是用方法表示的程序執行點;第二是用相對點表示的方位。如在Test.foo()方法執行前的連接點,執行點爲Test.foo(),方位爲該方法執行前的位置。Spring使用切點對執行點進行定位,而方位則在增強類型中定義。

  • 切點(PointCut)
    每個程序類都擁有多個連接點,如一個擁有兩個方法的類,這兩個方法都是連接點。
    但在這爲數衆多的連接點中,如何定位到某個感興趣的連接點上呢?AOP通過“切點”定位特定接連點。通過數據庫查詢的概念來理解切點和連接點的關係再適合不過了:連接點相當於數據庫中的記錄,而切點相當於查詢條件。切點和連接點不是一對一的關係,一個切點可以匹配多個連接點。 

    在Spring中,切點通過org.springframework.aop.Pointcut接口進行描述,它使用類和方法作爲連接點的查詢條件,Spring AOP的規則解析引擎負責解析切點所設定的查詢條件,找到對應的連接點。其實確切地說,用切點定位應該是執行點而非連接點,因爲連接點是方法執行前、執行後等包括方位信息的具體程序執行點,而切點只定位到某個方法上,所以如果希望定位到具體連接點上,還需要提供方位信息。  

  • 通知/增強(Advice)
    通知,個人感覺翻譯爲增強更貼切。
    增強是織入到目標類連接點上的一段程序代碼。
    在Spring中,增強除用於描述一段程序代碼外,還擁有另一個和連接點相關的信息,這便是執行點的方位。結合執行點方位信息和切點信息,我們就可以找到特定的連接點了!
    正因爲增強既包含了用於添加到目標連接點上的一段執行邏輯,又包含了用於定位連接點的方位信息,所以Spring所提供的增強接口都是帶方位名的:BeforeAdvice、AfterRetuningAdvice、ThrowsAdvice等。BeforeAdvice表示方法調用前的位置,而AfterReturingAdvice表示訪問返回後的位置。所以只有結合切點和增強兩者一起上陣才能確定特定的連接點並實施增強邏輯。


  • 切面(Aspect)
    切面由切點和增強(引介)組成,它既包括了橫切邏輯的定義,也包括了連接點的定義,Spring AOP就是負責實施切面的框架,它將切面所定義的橫切邏輯織入到切面所指定的連接點中。 

    AOP的工作重心在於如何將增強應用於目標對象的連接點上,這裏首先包括兩個工作:第一,如何通過切點和增強定位到連接點上;第二,如何在增強中編寫切面的代碼。本章大部分的內容都圍繞這兩點展開。

  • 目標對象
    增強邏輯的織入目標類。如果沒有AOP,目標業務類需要自己實現所有邏輯,就如中ForumService所示。在AOP的幫助下,ForumService只實現那些非橫切邏輯的程序邏輯,而性能監視和事務管理等這些橫切邏輯則可以使用AOP動態織入到特定的連接點上。

  • 引介( Introduction)
    引介是一種特殊的增強,它爲類添加一些屬性和方法。這樣,即使一個業務類原本沒有實現某個接口,通過AOP的引介功能,我們可以動態地爲該業務類添加接口的實現邏輯,讓業務類成爲這個接口的實現類。

  • 代理
    一個類被AOP織入增強後,就產出了一個結果類,它是融合了原類和增強邏輯的代理類。根據不同的代理方式,代理類既可能是和原類具有相同接口的類,也可能就是原類的子類,所以我們可以採用調用原類相同的方式調用代理類。 

  • 織入
    織入是將增強添加對目標類具體連接點上的過程,AOP像一臺織布機,將目標類、增強或者引介通過AOP這臺織布機天衣無縫地編織到一起。我們不能不說“織入”這個詞太精闢了。根據不同的實現技術,AOP有三種織入的方式: 
    1)編譯期織入,這要求使用特殊的Java編譯器; 
    2)類裝載期織入,這要求使用特殊的類裝載器; 
    3)動態代理織入,在運行期爲目標類添加增強生成子類的方式。 
    Spring採用動態代理織入,而AspectJ採用編譯期織入和類裝載期織入。



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