【轉】什麼是 AOP (面向方面編程)

AOP(Aspect-Oriented Programming,面向方面編程),可以說是OOP(Object-Oriented Programing,面向對象編程)的補充和完善。OOP引入封裝、繼承和多態性等概念來建立一種對象層次結構,用以模擬公共行爲的一個集合。當我們需要爲分散的對象引入公共行爲的時候,OOP則顯得無能爲力。也就是說,OOP允許你定義從上到下的關係,但並不適合定義從左到右的關係。例如日誌功能。日誌代碼往往水平地散佈在所有對象層次中,而與它所散佈到的對象的核心功能毫無關係。對於其他類型的代碼,如安全性、異常處理和透明的持續性也是如此。這種散佈在各處的無關的代碼被稱爲橫切(cross-cutting)代碼,在OOP設計中,它導致了大量代碼的重複,而不利於各個模塊的重用。

而AOP技術則恰恰相反,它利用一種稱爲“橫切”的技術,剖解開封裝的對象內部,並將那些影響了多個類的公共行爲封裝到一個可重用模塊,並將其名爲“Aspect”,即方面。所謂“方面”,簡單地說,就是將那些與業務無關,卻爲業務模塊所共同調用的邏輯或責任封裝起來,便於減少系統的重複代碼,降低模塊間的耦合度,並有利於未來的可操作性和可維護性。AOP代表的是一個橫向的關係,如果說“對象”是一個空心的圓柱體,其中封裝的是對象的屬性和行爲;那麼面向方面編程的方法,就彷彿一把利刃,將這些空心圓柱體剖開,以獲得其內部的消息。而剖開的切面,也就是所謂的“方面”了。然後它又以巧奪天功的妙手將這些剖開的切面復原,不留痕跡。

使用“橫切”技術,AOP把軟件系統分爲兩個部分:核心關注點和橫切關注點。業務處理的主要流程是核心關注點,與之關係不大的部分是橫切關注點。橫切關注點的一個特點是,他們經常發生在覈心關注點的多處,而各處都基本相似。比如權限認證、日誌、事務處理。Aop 的作用在於分離系統中的各種關注點,將核心關注點和橫切關注點分離開來。正如Avanade公司的高級方案構架師Adam Magee所說,AOP的核心思想就是“將應用程序中的商業邏輯同對其提供支持的通用服務進行分離。”

實現AOP的技術,主要分爲兩大類:一是採用動態代理技術,利用截取消息的方式,對該消息進行裝飾,以取代原有對象行爲的執行;二是採用靜態織入的方式,引入特定的語法創建“方面”,從而使得編譯器可以在編譯期間織入有關“方面”的代碼。然而殊途同歸,實現AOP的技術特性卻是相同的,分別爲:

1、join point(連接點):是程序執行中的一個精確執行點,例如類中的一個方法。它是一個抽象的概念,在實現AOP時,並不需要去定義一個join point。
2、point cut(切入點):本質上是一個捕獲連接點的結構。在AOP中,可以定義一個point cut,來捕獲相關方法的調用。
3、advice(通知):是point cut的執行代碼,是執行“方面”的具體邏輯。
4、aspect(方面):point cut和advice結合起來就是aspect,它類似於OOP中定義的一個類,但它代表的更多是對象間橫向的關係。
5、introduce(引入):爲對象引入附加的方法或屬性,從而達到修改對象結構的目的。有的AOP工具又將其稱爲mixin。

上述的技術特性組成了基本的AOP技術,大多數AOP工具均實現了這些技術。它們也可以是研究AOP技術的基本術語。

2.2.2 橫切技術

“橫切”是AOP的專有名詞。它是一種蘊含強大力量的相對簡單的設計和編程技術,尤其是用於建立鬆散耦合的、可擴展的企業系統時。橫切技術可以使得AOP在一個給定的編程模型中穿越既定的職責部分(比如日誌記錄和性能優化)的操作。

如果不使用橫切技術,軟件開發是怎樣的情形呢?在傳統的程序中,由於橫切行爲的實現是分散的,開發人員很難對這些行爲進行邏輯上的實現或更改。例如,用於日誌記錄的代碼和主要用於其它職責的代碼纏繞在一起。根據所解決的問題的複雜程度和作用域的不同,所引起的混亂可大可小。更改一個應用程序的日誌記錄策略可能涉及數百次編輯——即使可行,這也是個令人頭疼的任務。

在AOP中,我們將這些具有公共邏輯的,與其他模塊的核心邏輯糾纏在一起的行爲稱爲“橫切關注點(Crosscutting Concern)”,因爲它跨越了給定編程模型中的典型職責界限。

2.2.2.1 橫切關注點

一個關注點(concern)就是一個特定的目的,一塊我們感興趣的區域,一段我們需要的邏輯行爲。從技術的角度來說,一個典型的軟件系統包含一些核心的關注點和系統級的關注點。舉個例子來說,一個信用卡處理系統的核心關注點是借貸/存入處理,而系統級的關注點則是日誌、事務完整性、授權、安全及性能問題等,許多關注點——即橫切關注點(crosscutting concerns)——會在多個模塊中出現。如果使用現有的編程方法,橫切關注點會橫越多個模塊,結果是使系統難以設計、理解、實現和演進。AOP能夠比上述方法更好地分離系統關注點,從而提供模塊化的橫切關注點。

例如一個複雜的系統,它由許多關注點組合實現,如業務邏輯、性能,數據存儲、日誌和調度信息、授權、安全、線程、錯誤檢查等,還有開發過程中的關注點,如易懂、易維護、易追查、易擴展等,圖2.1演示了由不同模塊實現的一批關注點組成一個系統。

aop2.1.gif
圖2.1 把模塊作爲一批關注點來實現

通過對系統需求和實現的識別,我們可以將模塊中的這些關注點分爲:核心關注點和橫切關注點。對於核心關注點而言,通常來說,實現這些關注點的模塊是相互獨立的,他們分別完成了系統需要的商業邏輯,這些邏輯與具體的業務需求有關。而對於日誌、安全、持久化等關注點而言,他們卻是商業邏輯模塊所共同需要的,這些邏輯分佈於核心關注點的各處。在AOP中,諸如這些模塊,都稱爲橫切關注點。應用AOP的橫切技術,關鍵就是要實現對關注點的識別。

如果將整個模塊比喻爲一個圓柱體,那麼關注點識別過程可以用三棱鏡法則來形容,穿越三棱鏡的光束(指需求),照射到圓柱體各處,獲得不同顏色的光束,最後識別出不同的關注點。如圖2.2所示:

aop2.2.gif
圖2.2 關注點識別:三棱鏡法則

上圖識別出來的關注點中,Business Logic屬於核心關注點,它會調用到Security,Logging,Persistence等橫切關注點。

public class BusinessLogic
{
    public void SomeOperation()
    {
       //驗證安全性;Securtity關注點;
       //執行前記錄日誌;Logging關注點;

       DoSomething();

       //保存邏輯運算後的數據;Persistence關注點;
       //執行結束記錄日誌;Logging關注點;
    }
}

AOP的目的,就是要將諸如Logging之類的橫切關注點從BusinessLogic類中分離出來。利用AOP技術,可以對相關的橫切關注點封裝,形成單獨的“aspect”。這就保證了橫切關注點的複用。由於BusinessLogic類中不再包含橫切關注點的邏輯代碼,爲達到調用橫切關注點的目的,可以利用橫切技術,截取BusinessLogic類中相關方法的消息,例如SomeOperation()方法,然後將這些“aspect”織入到該方法中。例如圖2.3:

aop2.3.gif
圖2.3 將橫切關注點織入到核心關注點中

通過利用AOP技術,改變了整個系統的設計方式。在分析系統需求之初,利用AOP的思想,分離出核心關注點和橫切關注點。在實現了諸如日誌、事務管理、權限控制等橫切關注點的通用邏輯後,開發人員就可以專注於核心關注點,將精力投入到解決企業的商業邏輯上來。同時,這些封裝好了的橫切關注點提供的功能,可以最大限度地複用於商業邏輯的各個部分,既不需要開發人員作特殊的編碼,也不會因爲修改橫切關注點的功能而影響具體的業務功能。

爲了建立鬆散耦合的、可擴展的企業系統,AOP應用到的橫切技術,通常分爲兩種類型:動態橫切和靜態橫切。

2.2.2.2 動態橫切

動態橫切是通過切入點和連接點在一個方面中創建行爲的過程,連接點可以在執行時橫向地應用於現有對象。動態橫切通常用於幫助向對象層次中的各種方法添加日誌記錄或身份認證。在很多應用場景中,動態橫切技術基本上代表了AOP。

動態橫切技術的核心主要包括join point(連接點),point cut(切入點),advice(通知)和aspect(方面)。在前面,我已經概要地介紹了這些術語分別代表的含義。接下來,我將以一個具體的實例來進一步闡述它們在AOP動態橫切中實現的意義。

考慮一個電子商務系統,需要對訂單進行添加、刪除等管理操作。毫無疑問,在實際的應用場景中,這些行爲應與權限管理結合,只有獲得授權的用戶方能夠實施這些行爲。採用傳統的設計方法,其僞代碼如下:
public class OrderManager
{
    private ArrayList m_Orders;
    public OrderManager()
    {
       m_Orders = new ArrayList();
    }
    public void AddOrder(Order order)
    {
        if (permissions.Verify(Permission.ADMIN))
        {

            m_Orders.Add(order);
        }
    }

    public void RemoveOrder(Order order)
    {
        if (permissions.Verify(Permission.ADMIN))
        {
            m_Orders.Remove(order);
        }
    }
}

同樣的,在該電子商務系統中,還需要對商品進行管理,它採用了同樣的授權機制:
public class ProductManager
{
    private ArrayList m_Products;
    public ProductManager()
    {
        m_Products = new ArrayList();
    }
    public void AddProduct(Product product)
    {
        if (permissions.Verify(Permission.ADMIN))
        {
             m_Products.Add(product);
        }
    }
    public void RemoveProduct(Product product)
    {
        if (permissions.Verify(Permission.ADMIN))
        {
             m_Products.Remove(product);
        }
    }
}

如此以來,在整個電子商務系統中,核心業務包括訂單管理和商品管理,它們都需要相同的權限管理,如圖2.4所示:

aop2.4.gif
圖2.4 電子商務系統的權限驗證實現

毫無疑問,利用AOP技術,我們可以分離出系統的核心關注點和橫切關注點,從橫向的角度,截取業務管理行爲的內部消息,以達到織入權限管理邏輯的目的。當執行AddOrder()等方法時,系統將驗證用戶的權限,調用橫切關注點邏輯,因此該方法即爲AOP的join point。對於電子商務系統而言,每個需要權限驗證的方法都是一個單獨的join point。由於權限驗證將在每個方法執行前執行,所以對於這一系列join point,只需要定義一個point cut。當系統執行到join point處時,將根據定義去查找對應的point cut,然後執行這個橫切關注點需要實現的邏輯,即advice。而point cut和advice,就組合成了一個權限管理aspect。

aop2.5.gif
圖2.5 AOP動態橫切的技術實現

由於aspect是一個封裝的對象,我們可以定義這樣一個aspect:
private static aspect AuthorizationAspect{……}

然後在這個aspect中定義point cut,在point cut中,定義了需要截取上下文消息的方法,例如:
private pointcut authorizationExecution():
execution(public void OrderManager.AddOrder(Order)) ||
execution(public void OrderManager.DeleteOrder(Order)) ||
execution(public void ProductManager.AddProduct(Product)) ||
execution(public void ProductManager.DeleteProduct(Product));

由於權限驗證是在訂單管理方法執行之前完成,因此在before advice中,定義權限檢查:
before(): authorizationExecution()
{
    if !(permissions.Verify(Permission.ADMIN))
    {
        throw new UnauthorizedException();
    }
}

通過定義了這樣一個完整的aspect,當系統調用OrderManager或ProductManager的相關方法時,就觸發了point cut,然後調用相應的advice邏輯。如此以來,OrderManager和ProductManager模塊就與權限管理模塊完全解除了依賴關係,同時也消除了傳統設計中不可避免的權限判斷的重複代碼。這對於建立一個鬆散耦合、可擴展的系統軟件是非常有利的。

2.2.2.3 靜態橫切

靜態橫切和動態橫切的區別在於它不修改一個給定對象的執行行爲。相反,它允許通過引入附加的方法字段和屬性來修改對象的結構。此外,靜態橫切可以把擴展和實現附加到對象的基本結構中。在AOP實現中,通常將靜態橫切稱爲introduce或者mixin。

靜態橫切在AOP技術中,受到的關注相對較少。事實上,這一技術蘊含的潛力是巨大的。使用靜態橫切,架構師和設計者能用一種真正面向對象的方法有效地建立複雜系統的模型。靜態橫切允許您不用創建很深的層次結構,以一種本質上更優雅、更逼真於現實結構的方式,插入跨越整個系統的公共行爲。尤其是當開發應用系統時,如果需要在不修改原有代碼的前提下,引入第三方產品和API庫,則靜態橫切技術將發揮巨大的作用。

舉例來說,當前已經實現了一個郵件收發系統,其中類Mail完成了收發郵件的功能。但在產品交付後,發現該系統存在缺陷,在收發郵件時,未曾實現郵件地址的驗證功能。現在,第三方產品已經提供了驗證功能的接口IValidatable:
public interface IValidatable
{
    bool ValidateAddress();
}

我們可以利用設計模式中的Adapter模式,來完成對第三方產品API的調用。我們可以定義一個新的類MailAdapter,該類實現了IValidatable接口,同時繼承了Mail類:
public class MailAdapter:Mail,IValidatable
{
     public bool ValidateAddress()
     {
         if(this.getToAddress() != null)
         {
             return true;
         }
         else
         {
             return false;
         }
     }
}

通過引入MailAdapter類,原來Mail對象完成的操作,將全部被MailAdapter對象取代。然而,此種實現方式雖然能解決引入新接口的問題,但類似下面的代碼,卻是無法編譯通過的:
Mail mail = new Mail();
IValidatable validate = ((IValidatable)mail).ValidateAddress();

必須將第一行代碼作如下修改:
Mail mail = new MailAdapter();

利用AOP的靜態橫切技術,可以將IValidatable接口織入到原有的Mail類中,這是一種非常形象的introduce功能,其實現仍然是在aspect中完成:
import com.acme.validate.Validatable;

public aspect MailValidateAspect
{
    declare parents: Mail implements IValidatable;

    public boolean Mail.validateAddress()
    {
         if(this.getToAddress() != null)
         {
              return true;
         }
         else
         {
              return false;
         }
    }
}

靜態橫切的方法,並沒有引入類似MailAdapter的新類,而是通過定義的MailValidateAspect方面,利用橫切技術爲Mail類introduce了新的方法ValidateAddress(),從而實現了Mail的擴展。因此如下的代碼完全可行。
Mail mail = new Mail();
IValidatable validate = ((IValidatable)mail).ValidateAddress();

2.3 AOP技術的優勢

AOP技術的優勢是顯而易見的。在面向對象的世界裏,人們提出了各種方法和設計原則來保障系統的可複用性與可擴展性,以期建立一個鬆散耦合、便於擴展的軟件系統。例如GOF提出的“設計模式”,爲我們提供了設計的典範與準則。設計模式通過最大程度的利用面向對象的特性,諸如利用繼承、多態,對責任進行分離、對依賴進行倒置,面向抽象,面向接口,最終設計出靈活、可擴展、可重用的類庫、組件,乃至於整個系統的架構。在設計的過程中,通過各種模式體現對象的行爲、暴露的接口、對象間關係、以及對象分別在不同層次中表現出來的形態。然而鑑於對象封裝的特殊性,“設計模式”的觸角始終在接口與抽象中大做文章,而對於對象內部則無能爲力。

通過“橫切”技術,AOP技術就能深入到對象內部翻雲覆雨,截取方法之間傳遞的消息爲我所用。由於將核心關注點與橫切關注點完全隔離,使得我們能夠獨立的對“方面”編程。它允許開發者動態地修改靜態的OO模型,構造出一個能夠不斷增長以滿足新增需求的系統,就象現實世界中的對象會在其生命週期中不斷改變自身,應用程序也可以在發展中擁有新的功能。

設計軟件系統時應用AOP技術,其優勢在於:

(一)在定義應用程序對某種服務(例如日誌)的所有需求的時候。通過識別關注點,使得該服務能夠被更好的定義,更好的被編寫代碼,並獲得更多的功能。這種方式還能夠處理在代碼涉及到多個功能的時候所出現的問題,例如改變某一個功能可能會影響到其它的功能,在AOP中把這樣的麻煩稱之爲“糾結(tangling)”。

(二)利用AOP技術對離散的方面進行的分析將有助於爲開發團隊指定一位精於該項工作的專家。負責這項工作的最佳人選將可以有效利用自己的相關技能和經驗。

(三)持久性。標準的面向對象的項目開發中,不同的開發人員通常會爲某項服務編寫相同的代碼,例如日誌記錄。隨後他們會在自己的實施中分別對日誌進行處理以滿足不同單個對象的需求。而通過創建一段單獨的代碼片段,AOP提供瞭解決這一問題的持久簡單的方案,這一方案強調了未來功能的重用性和易維護性:不需要在整個應用程序中一遍遍重新編寫日誌代碼,AOP使得僅僅編寫日誌方面(logging aspect)成爲可能,並且可以在這之上爲整個應用程序提供新的功能。

總而言之,AOP技術的優勢使得需要編寫的代碼量大大縮減,節省了時間,控制了開發成本。同時也使得開發人員可以集中關注於系統的核心商業邏輯。此外,它更利於創建鬆散耦合、可複用與可擴展的大型軟件系統。

 

附知乎的一段回答:

面向切面編程(AOP是Aspect Oriented Program的首字母縮寫) ,我們知道,面向對象的特點是繼承、多態和封裝。而封裝就要求將功能分散到不同的對象中去,這在軟件設計中往往稱爲職責分配。實際上也就是說,讓不同的類設計不同的方法。這樣代碼就分散到一個個的類中去了。這樣做的好處是降低了代碼的複雜程度,使類可重用。
但是人們也發現,在分散代碼的同時,也增加了代碼的重複性。什麼意思呢?比如說,我們在兩個類中,可能都需要在每個方法中做日誌。按面向對象的設計方法,我們就必須在兩個類的方法中都加入日誌的內容。也許他們是完全相同的,但就是因爲面向對象的設計讓類與類之間無法聯繫,而不能將這些重複的代碼統一起來。
也許有人會說,那好辦啊,我們可以將這段代碼寫在一個獨立的類獨立的方法裏,然後再在這兩個類中調用。但是,這樣一來,這兩個類跟我們上面提到的獨立的類就有耦合了,它的改變會影響這兩個類。那麼,有沒有什麼辦法,能讓我們在需要的時候,隨意地加入代碼呢?這種在運行時,動態地將代碼切入到類的指定方法、指定位置上的編程思想就是面向切面的編程。
一般而言,我們管切入到指定類指定方法的代碼片段稱爲切面,而切入到哪些類、哪些方法則叫切入點。有了AOP,我們就可以把幾個類共有的代碼,抽取到一個切片中,等到需要時再切入對象中去,從而改變其原有的行爲。
這樣看來,AOP其實只是OOP的補充而已。OOP從橫向上區分出一個個的類來,而AOP則從縱向上向對象中加入特定的代碼。有了AOP,OOP變得立體了。如果加上時間維度,AOP使OOP由原來的二維變爲三維了,由平面變成立體了。從技術上來說,AOP基本上是通過代理機制實現的。
AOP在編程歷史上可以說是里程碑式的,對OOP編程是一種十分有益的補充。
 
 
 

原文鏈接:https://www.cnblogs.com/zhugenqiang/archive/2008/07/27/1252761.html

參考連接:http://wayfarer.cnblogs.com/articles/241012.html

            http://www.cnblogs.com/zhenyulu/zhenyulu/articles/234074.html

 

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