本文首先介紹AOP(面向方面編程)的相關概念及理論,而後介紹如何使用PostSharp框架在.NET平臺上實現AOP,最後對PostSharp的機制及AOP的優劣進行一個簡單的分析。html
根據維基百科的定義,「AOP(Aspect-Oriented Programming)是一種將函數的輔助性功能與業務邏輯相分離的編程泛型(programming paradigm),其目的是將橫切關注點(cross-cutting concerns)分離出來,使得程序具備更高的模塊化特性。AOP是面向方面軟件開發(Aspect-Oriented Software Development)在編碼實現層面上的具體表現(面向方面軟件開發AOSD是一個囊括面向方面分析、面向方面設計和麪向方面編程等一系列概念的完整工程系統——筆者注)。AOP包括編程模型和具體用於實現AOP的框架兩部分。」程序員
下面對上文提到的定義進行一些解釋。web
在當前大多數支持面向對象的編程語言中(例如C#,Java等),函數(Function)是表述程序功能的最小單元,而一個函數的代碼層面每每同時含有核心業務邏輯和輔助性功能。核心業務邏輯指一個函數自己主要要實現的業務功能,例如在一個在線電子商務系統中,「PlaceOrder」函數其核心業務邏輯是「下訂單」,而「UpgradeMember」函數其核心業務是「提高一個會員的等級」。可是,一個函數除了核心業務代碼外,每每還會有一些輔助性功能代碼,如事務處理、緩存處理、日誌記錄、異常處理等等。而這些輔助性功能通常會存在於大多數甚至全部業務函數中,即造成AOSD中所謂的橫切關注點,如圖1所示。編程
圖一、橫切關注點示意緩存
橫切關注點的存在,形成了以下幾個問題。框架
橫切關注點不只橫切各個函數,還可能在不一樣類甚至不一樣工程間橫切,使得同一個輔助功能(如事務處理)分散到各處,若是要增長新函數時要時刻注意別忘了添加全部須要的橫切代碼。另外,若是須要對其進行修改,則須要到全部被橫切的函數中修改,維護難度極大。編程語言
因爲同一個輔助性功能的代碼幾乎是徹底相同的,這樣就會令一樣的代碼在各個函數中出現,引入了大量冗餘代碼。ide
橫切關注點令核心業務代碼和輔助性代碼雜糅糾纏在一塊兒,破壞了業務函數代碼的純淨性和函數職責的單一性,引入了大量繁雜的代碼和結構,使得代碼質量降低。模塊化
因此,AOP的核心思想就是在編寫代碼時將橫切關注點分離出來,造成單獨的模塊,單獨編寫和維護,再也不分散到各業務函數,使得業務函數僅包含核心業務代碼,從而解決以上問題。而在程序編譯或運行時,經過某些手段(下文介紹)令獨立的橫切關注點代碼能夠與核心業務代碼自動協做運行,完成自己須要的功能。函數
一個Aspect指上文提到的橫切關注點在編程中的具體實現,它包含一個橫切關注點所須要實現的具體輔助功能。具體到代碼中,Aspect可能會被實現爲一個Class,一個Function或一個Attribute。
鏈接點指一個業務函數代碼中的一個位置或時機,在這個位置或時機容許Aspect代碼插入執行。常見的鏈接點有進入函數執行業務代碼前時、執行徹底部業務代碼離開函數前、當有異常發生在異常處理代碼執行前等等。
織入指將指定的Aspect代碼插入指定鏈接點,使得橫切代碼與業務代碼交合在一塊兒。
JPM主要是面向方面語言(如AspectJ)或面向方面框架的語義模型。主要包含如下三點:有哪些可用鏈接點,如何指定鏈接點以及如何織入。
通常來講,在純編譯型語言(如C、C++)等語言中實現AOP很是困難,必須徹底從編譯器角度入手。本文主要討論託管型語言(如C#,Java)中AOP的實現方式。AOP的主要實現方式有編譯時AOP和運行時AOP兩種,下面分別介紹。
編譯時AOP的實現思想是給語言的編譯器作擴展,使得在編譯程序的時候編譯器將相應的Aspect代碼織入到業務代碼的指定鏈接點,輸出整合的結果。圖2是編譯時AOP的示意圖(以.NET平臺爲例)。
圖二、編譯時AOP示意圖
如圖2所示,當使用靜態織入時,帶AOP擴展的編譯器會在編譯時將Aspect代碼織入業務函數代碼,造成整合後的IL,而後交由CLR運行。
運行時AOP如圖3所示。
圖三、運行時AOP的示意圖
如圖3所示,運行時AOP的實現方式是將擴展添加到運行虛擬機而不是編譯器。Aspect和業務代碼分別獨立編譯,而在運行時由虛擬機在必要時進行織入。
PostSharp是一個用於在.NET平臺上實現AOP的框架,是我比較經常使用的一個AOP框架,官方網站爲http://www.sharpcrafters.com。目前最新版本爲2.0,可是2.0的license再也不免費,所以我的建議下載1.5版,同時下文都是基於PostSharp1.5。
PostSharp使用靜態織入方式實現AOP,其鏈接點很是豐富,使用簡單,並且相對其它一些.NET平臺上的AOP框架來講,PostSharp較爲輕量級,可是功能卻一點也不遜色,所以是我比較喜歡的一個AOP框架。更多關於PostSharp的介紹請參看其官方網站。
另外使用PostSharp與其它框架不太同樣的是必定要下載安裝包安裝,只引用類庫是不行的,由於上文說過,AOP框架須要爲編譯器或運行時添加擴展。
這一節將經過一個例子演示如何使用PostSharp在.NET平臺上實現AOP。這個例子將經過AOP爲核心業務函數增長日誌記錄功能。
首先新建一個C#的WinForm應用程序,如圖4所示,這裏將工程命名爲「PostSharpExample」。
圖四、新建項目
首先咱們來編寫核心業務。固然這裏不存在真正的業務,咱們只是模擬一個而已。將要模擬的核心業務是預約房間。先構建一個如圖5所示的簡單UI。
圖五、UI界面
下面咱們爲項目增長一個「CoreBusiness」類,並在其中添加「Subscribe」方法。代碼以下:
1
2
3
4
5
6
7
8
9
10
11
12
|
using
System;
namespace
PostSharpExample
{
public
class CoreBusiness
{
public
static void Describe(string memberName, string roomNumber)
{
System.Windows.Forms.MessageBox.Show(String.Format(
"尊敬的會員{0},恭喜您預約房間{1}成功!"
, memberName, roomNumber),
"提示"
);
}
}
}
|
能夠看到,這裏Subscribe方法僅僅是輸出一個提示框。固然,在真正項目中這種輸出型代碼不該該寫在業務邏輯中,這裏這樣寫主要是爲了演示方便。而後,咱們在Form1中調用Subscribe業務方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
using
System;
using
System.Windows.Forms;
namespace
PostSharpExample
{
public
partial class Form1 : Form
{
public
Form1()
{
InitializeComponent();
}
private
void BTN_SUBSCRIBE_Click(object sender, EventArgs e)
{
if
(!String.IsNullOrEmpty(TXB_NAME.Text.Trim()) && !String.IsNullOrEmpty(TXB_ROOM.Text.Trim()))
CoreBusiness.Describe(TXB_NAME.Text.Trim(), TXB_ROOM.Text.Trim());
else
MessageBox.Show(
"信息不完整"
,
"提示"
);
}
}
}
|
運行程序就能夠看到相應的效果:
圖六、預約房間成功演示效果
如今加入咱們要爲程序添加日誌功能,記錄業務函數的執行狀況。這裏咱們假定須要將日誌記錄到純文本文件中,首先咱們完成日誌記錄工具類,LoggingHelper。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
using
System;
using
System.IO;
namespace
PostSharpExample
{
class
LoggingHelper
{
private
const String _errLogFilePath = @ "log.txt" ;
public
static void Writelog(String message)
{
StreamWriter sw =
new
StreamWriter(_errLogFilePath, true );
String logContent = String.Format(
"[{0}]{1}"
, DateTime.Now.ToString(
"yyyy-MM-dd hh:mm:ss"
), message);
sw.WriteLine(logContent);
sw.Flush();
sw.Close();
}
}
}
|
若是不使用AOP,則咱們要爲包括Subscribe在內的每個方法在覈心業務代碼的先後插入日誌記錄代碼(Writelog),咱們看看使用PostSharp如何將這種橫切關注點分離出來。由於要使用PostSharp,因此要先添加對PostSharp庫文件的引用,安裝過PostSharp後,在系統可引用項中會多出「PostSharp.Laos」、「PostSharp.Public」和「PostSharp.AspNet」,這裏咱們作的是Winform程序,因此只需添加對「PostSharp.Laos」和「PostSharp.Public」的引用便可。
下面咱們就要寫Aspect了,PostSharp的Aspect是使用Attribute實現的,下面是我實現的日誌記錄Aspect代碼。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
using
System;
using
PostSharp.Laos;
namespace
PostSharpExample
{
[Serializable]
[AttributeUsage(AttributeTargets.Method, AllowMultiple =
true
, Inherited =
true
)]
public
sealed class LoggingAttribute : OnMethodBoundaryAspect
{
public
string BusinessName { get; set; }
public
override void OnEntry(MethodExecutionEventArgs eventArgs)
{
LoggingHelper.Writelog(BusinessName +
"開始執行"
);
}
public
override void OnExit(MethodExecutionEventArgs eventArgs)
{
LoggingHelper.Writelog(BusinessName +
"成功完成"
);
}
}
}
|
咱們約定每一個Aspect類的命名必須爲「XXXAttribute」的形式。其中「XXX」就是這個Aspect的名字。PostSharp中提供了豐富的內置「Base Aspect」以便咱們繼承,其中這裏咱們繼承「OnMethodBoundaryAspect 」,這個Aspect提供了進入、退出函數等鏈接點方法。另外,Aspect上必須設置「[Serializable] 」,這與PostSharp內部對Aspect的生命週期管理有關,具體爲何請參看這裏。
咱們的LoggingAttribute很是簡單,就是在進入(Entry)和離開(Exit)函數時分別記錄日誌到log文件。如今咱們把這個Aspect應用到業務方法上:
1
2
3
4
5
|
[Logging(BusinessName=
"預約房間"
)]
public
static void Describe(string memberName, string roomNumber)
{
System.Windows.Forms.MessageBox.Show(String.Format(
"尊敬的會員{0},恭喜您預約房間{1}成功!"
, memberName, roomNumber),
"提示"
);
}
|
能夠看到,應用Aspect很是簡單,就是將相應的Attribute加到業務方法上面。如今咱們再運行預約房間程序,結果和上次沒什麼兩樣,可是若是咱們打開程序目錄,會看到多了一個「log.txt」文件,裏面記錄有相似圖7的內容。
圖七、日誌內容
能夠看到,咱們已經經過AOP實現了日誌記錄功能。經過AOP將橫切關注點分離出來後,日誌記錄的代碼都放在LoggingAttribute裏,須要修改只要修改一處便可。同時,業務方法僅含有業務代碼,這樣大大提升了程序代碼的可讀性和可維護性。
上文已經說到,PostSharp使用的是靜態織入技術,下面咱們分析一下PostSharp是如何實現的。
首先,當安裝PostSharp時,它自動爲Visual Studio編譯器添加了AOP擴展。若是仔細觀察PostSharpExample編譯信息,會發現有這麼兩行:
圖八、PostSharp編譯信息
很明顯,在.NET Complier編譯完成後,下面PostSharp又作了一部分工做,這部分工做就是靜態織入的過程。若是咱們用.NET Reflector查看PostSharpExample.exe中Subscribe方法的反編譯代碼,會發現多了不少東西:
圖九、織入Aspect後的Describe代碼(由.NET Reflector反編譯)
這些多出來的代碼,就是PostSharp靜態織入進去的。固然,這些代碼在每次編譯完成後,PostSharp都會從新織入一次,因此整個過程對程序員是透明的,咱們只需維護純淨的業務代碼和Aspect代碼便可。
整體來講,使用PostSharp,將會帶來以下優勢:
固然,使用PostSharp也不是沒有缺點,主要缺點有以下兩方面:
因此,對因而否引入AOP,請根據項目具體狀況,權衡而定。
本文只是簡要介紹了PostSharp以及實現了一個小例子,並不打算詳細完整地介紹PostSharp的方方面面,而只想起到一個拋磚引玉的做用。PostSharp還有很是豐富的功能等待各位學習,所以,若是您對PostSharp十分有興趣,想進一步學習,請參看PostSharp官方參考文檔。
本文用到的Example請點擊這裏下載。
PostSharp1.5安裝包請點擊這裏下載。