AOP已经不是一个什么新名词了,在博客园使用关键字搜索可以查出n多条关于AOP的介绍,这里就不再赘述了。
在Bruce Zhang's Blog里面有很多关于AOP介绍及其在.net下实现研究,总觉得如果什么都从头来写难免有自造轮子的嫌疑,但是目前也没有很成熟的AOP框架让我们能轻松完成基于AOP架构,不过一直以来都在关注的PostSharp开源项目日趋成熟,目前已发布了PostSharp 1.0 (Beta release 3)。即使如此,也还没能到应用到产品上的时候。
前段时间一直在封装一个权限系统,时常为如何给调用方提供一个良好的编程接口烦恼,加之前前段时间考虑的日志、异常接管、事务、缓存等等一些横向组件的架构分析,自然就想用AOP技术实现,但是由于实现难度实在不小作罢;这两天又重新学习研究了PostSharp的架构与实现思想,觉得还是尝试一下,将其融入现有框架;
早在年初就有不少前辈大师就如何使用这个东西撰写过文章,如Q.yuhen的PostSharp - Lightweight Aspect-Oriented System该仁兄下面见解很到位:
和以往基于 Dynamic Proxy 方式与 AOP 解决方案做个比较。
- 由于采用 MSIL Injection,因此静态代码注入的执行效率要高于使用 Reflection Emit。
- 使用 MSBuild Task,使得开发人员可以像使用编译器内置 Attribute 那样使用 AOP。
- 可以拦截任意方法,而 Dynamic Proxy 方式的 AOP 往往采取继承方式来拦截 Virtual 方法。
- 拥有更多的控制权。包括中断执行流程,修改参数和返回值等等。
- 还可以拦截 Field Access、Exception 等操作。
- 无需将对象创建代码改成 "new proxy()",更加透明。
- 可以使用通配符进行多重拦截匹配。
- 静态注入带来的问题更多的是注入代码的质量和调试复杂度。
另外有一老外的Using AOP and PostSharp to Enhance Your CodeAB两部分,相当精彩,本文就是在参考这两篇好文的基础上做的。
我们假设有这么个场景,其实这也是实际业务中很常见的处理方式:有一定单管理模块,具备新增、删除两功能,我们在新增删除的时候必须校验权限,在删除的时候还必须记录日志,出现异常了还必须捕捉并记录异常;
按以前的写法我们可能很麻烦,我们要如此这般的写:
public
class
Orders
{
public
bool
Add(
string
id,
string
orderName)
{
try
{
if
(User.AddEnable)
{
//
TODO:新增订单的实现
Console.WriteLine(
"
正在执行新增订单方法的操作,回车继续……
"
);
Console.ReadLine();
Console.WriteLine(
"
您添加订单成功:编号:{0},名称:{1}
"
, id, orderName);
return
true
;
}
else
{
//
}
}
catch
(Exception)
{
//
TODO:记录异常的实现
throw
;
}
return
true
;
}
public
bool
Delete(
string
id)
{
try
{
if
(User.DeleteEnable)
{
//
TODO:删除订单的实现
Console.WriteLine(
"
您删除订单成功:编号:{0}
"
, id);
}
else
{
//
}
}
catch
(Exception)
{
//
TODO:记录异常的实现
throw
;
}
return
true
;
}
这种写的弊端我就不多说了,有很多先驱都阐述过……
我要演示的是采用AOP技术的框架原型实现:
首先我们应该安装PostSharp(一定要安装要不能没办法注入处理代码)
然后我们实现Orders对象
using
System;
namespace
PostSharp.Demo
{
public
class
Orders
{
[Permission]
[Exception]
public
bool
Add(
string
id,
string
orderName)
{
Console.WriteLine(
"
正在执行新增订单方法的操作,回车继续……
"
);
Console.ReadLine();
Console.WriteLine(
"
您添加订单成功:编号:{0},名称:{1}
"
, id, orderName);
return
true
;
}
[Logger]
[Permission]
[Exception]
public
bool
Delete(
string
id)
{
Console.WriteLine(
"
您删除订单成功:编号:{0}
"
, id);
return
true
;
}
}
}
当然还要模拟一个用户资格认证
namespace
PostSharp.Demo
{
///
<summary>
///
静态的用户对象,用于存放当前登录用户,成员资格
///
</summary>
public
static
class
User
{
private
static
string
_userId;
public
static
string
UserId
{
get
{
return
_userId; }
set
{ _userId
=
value; }
}
public
static
bool
AddEnable
{
get
{
return
(_userId.ToLower()
==
"
admin
"
);
}
}
public
static
bool
DeleteEnable
{
get
{
return
(_userId.ToLower()
==
"
admin
"
);
}
}
}
}
再然后我们实现
权限控制方面PermissionAttribute,日志方面LoggerAttribute,异常处理方面ExceptionAttribute……
PermissionAttribute
using
System;
using
PostSharp.Laos;
namespace
PostSharp.Demo
{
[Serializable]
[global::System.AttributeUsage(AttributeTargets.All, Inherited
=
true
, AllowMultiple
=
false
)]
public
class
PermissionAttribute : OnMethodBoundaryAspect
{
public
override
void
OnEntry(MethodExecutionEventArgs eventArgs)
{
if
(
!
User.AddEnable)
{
Console.WriteLine(
"
用户:【{0}】没有权限:【{1}】
"
, User.UserId, eventArgs.Method);
eventArgs.FlowBehavior
=
FlowBehavior.Return;
}
}
}
}
LoggerAttribute
using
System;
using
PostSharp.Laos;
namespace
PostSharp.Demo
{
[Serializable]
[global::System.AttributeUsage(AttributeTargets.All, Inherited
=
true
, AllowMultiple
=
false
)]
public
sealed
class
LoggerAttribute : OnMethodInvocationAspect
{
public
override
void
OnInvocation(MethodInvocationEventArgs eventArgs)
{
DateTime time
=
DateTime.Now;
string
log
=
"
时间:{0},操作人员:{1},操作:{2}!
"
;
object
[] arg
=
eventArgs.GetArguments();
log
=
String.Format(log, time, User.UserId,
"
删除Id为
"
+
arg[
0
].ToString()
+
"
的订单!
"
);
System.IO.File.WriteAllText(
"
C:\\Log.Txt
"
, log);
}
}
}
ExceptionAttribute
using
System;
using
PostSharp.Laos;
namespace
PostSharp.Demo
{
[Serializable]
[global::System.AttributeUsage(AttributeTargets.All, Inherited
=
true
, AllowMultiple
=
false
)]
public
class
ExceptionAttribute : OnExceptionAspect
{
public
override
void
OnException(MethodExecutionEventArgs eventArgs)
{
Console.WriteLine(
"
程序出现异常:{0}
"
, eventArgs.Exception.Message);
eventArgs.FlowBehavior
=
FlowBehavior.Return;
}
}
}
然后再用控制台程序测试下能不能成功
Orders order
=
new
Orders();
Console.WriteLine(
"
请输入用户名:
"
);
User.UserId
=
Console.ReadLine();
Console.WriteLine(
"
请输入密码:
"
);
Console.ReadLine();
string
id;
LRedo:
Console.WriteLine(
"
请输入您要执行的操作:新增(A),删除(D),退出(X)
"
);
string
opt
=
Console.ReadLine();
if
(opt.ToLower()
==
"
a
"
)
{
Console.WriteLine(
"
请输入订单编号:
"
);
id
=
Console.ReadLine();
Console.WriteLine(
"
请输入订单名称:
"
);
string
name
=
Console.ReadLine();
order.Add(id, name);
}
else
if
(opt.ToLower()
==
"
d
"
)
{
Console.WriteLine(
"
请输入订单编号:
"
);
id
=
Console.ReadLine();
order.Delete(id);
}
else
if
(opt.ToLower()
==
"
x
"
)
{
}
else
{
Console.WriteLine(
"
您的输入不正确,请重新输入!
"
);
goto
LRedo;
}
Console.WriteLine(
"
按任意键退出……
"
);
Console.ReadLine();
写完这些我们再反编译一下生成的exe文件,发现里面的Orders成了这模样了,神奇了?
反编译后的代码
代码很简单,我是采用控制台应用实现的,如果您有兴趣,请
下载Demo源码玩玩。