.NET AOP (六)在编译阶段PostSharp
转载至:点击打开链接
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”。
图4、新建项目
首先我们来编写核心业务。当然这里不存在真正的业务,我们只是模拟一个而已。将要模拟的核心业务是预定房间。先构建一个如图5所示的简单UI。
图5、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(
"信息不完整"
,
"提示"
);
}
}
}
|
运行程序就可以看到相应的效果:
图6、预定房间成功演示效果
现在加入我们要为程序添加日志功能,记录业务函数的执行情况。这里我们假定需要将日志记录到纯文本文件中,首先我们完成日志记录工具类,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的内容。
图7、日志内容
可以看到,我们已经通过AOP实现了日志记录功能。通过AOP将横切关注点分离出来后,日志记录的代码都放在LoggingAttribute里,需要修改只要修改一处即可。同时,业务方法仅含有业务代码,这样大大提高了程序代码的可读性和可维护性。
上文已经说到,PostSharp使用的是静态织入技术,下面我们分析一下PostSharp是如何实现的。
首先,当安装PostSharp时,它自动为Visual Studio编译器添加了AOP扩展。如果仔细观察PostSharpExample编译信息,会发现有这么两行:
图8、PostSharp编译信息
很明显,在.NET Complier编译完成后,下面PostSharp又做了一部分工作,这部分工作就是静态织入的过程。如果我们用.NET Reflector查看PostSharpExample.exe中Subscribe方法的反编译代码,会发现多了很多东西:
图9、织入Aspect后的Describe代码(由.NET Reflector反编译)
这些多出来的代码,就是PostSharp静态织入进去的。当然,这些代码在每次编译完成后,PostSharp都会重新织入一次,所以整个过程对程序员是透明的,我们只需维护纯净的业务代码和Aspect代码即可。
总体来说,使用PostSharp,将会带来如下优点:
当然,使用PostSharp也不是没有缺点,主要缺点有如下两方面:
所以,对于是否引入AOP,请根据项目具体情况,权衡而定。
本文只是简要介绍了PostSharp以及实现了一个小例子,并不打算详细完整地介绍PostSharp的方方面面,而只想起到一个抛砖引玉的作用。PostSharp还有非常丰富的功能等待各位学习,因此,如果您对PostSharp十分有兴趣,想进一步学习,请参看PostSharp官方参考文档。
本文用到的Example请点击这里下载。
PostSharp1.5安装包请点击这里下载。