** 基于 .Net 的 AOP 实现技术 **
** 前言 **
在笔者的《 面向对象的应用服务层设计 》一文中,笔者讨论了软件系统中设计应用服务层所需要考虑的问题,以及系统分层的基本思考方法。这些讨论作关注的问题,都是系统中纵向的层次的划分。然而,在设计软件系统的时候,我们不仅仅要考虑纵向的关系,很多时候,我们还需要关注所谓的“横切关注点”的问题,例如,存在于系统每个部分的日志记录、安全性验证等。 AOP (面向方面编程)的出现,便是为了解决这些“横切关注点”的问题。
虽然 AOP 目前还不是如 OOP 般非常成熟,但是,也已经有数个支持 AOP 的产品问世,其中比较有名的有 AspectJ , AspectWerkz 等,这些产品都基于 Java 平台。在 .Net 平台上,也有一些实现,如 LOOM 等,但是,相对于 Java 平台的实现,这些实现都还很不成熟,功能也比较弱,使用上也不是很方便。因此,笔者在 .Net 平台下自己实现了一个轻量级的 AOP 框架,现在拿出来同大家共同探讨。
在本文中,笔者将首先归纳一下目前实现 AOP 的基本手段,然后,给出一个已经实现的 AOP 的范例( WebsharpAspect ),并提供全部源代码,这是一个轻量级的 AOP 实现,虽然目前功能还不是很强大,实现也比较简单,但是,他已经可以完成大部分我们所需要的 AOP 功能,你可以在项目中直接使用他。重要的是,其中展示了在 .Net 环境下实现 AOP 的一些技术和思路,笔者也希望通过提供源代码的方式,能够对大家学习 AOP 有所助益。
** 实现 AOP ** ** 的方法 **
实现 AOP 的关键,是拦截正常的方法调用,将我们需要额外附加的功能透明的“织入”到这些方法中,以完成一些额外的要求。从总体方法上来说,织入的方法有两大类:静态织入和动态织入。
静态织入方法,一般都是需要扩展编译器的功能,将需要织入的代码,通过修改字节码( Java )或者 IL 代码( .Net )的方法,直接添加到相应的被织入点;或者,我们需要为原来语言添加新的语法结构,从语法上支持 AOP 。 AspectJ 就是采用的这种方式。使用这种方式来实现 AOP ,其优点是代码执行的效率高,缺点是实现者需要对虚拟机有很深的了解,才能够做到对字节码修改。由于织入方法是静态的,当需要添加新的织入方法时,往往需要重新编译,或者说运行字节码增强器重新执行静态织入的方法。当然,在 .Net 平台上,我们也可以使用 Emit 提供的强大功能来实现这一点。另外,字节码增强器带来了很大的不透明性,程序员很难直观的调试增强后的字节码,因此很多程序员总是在心理上抵制这种字节码增强器。
动态织入的方法,具体实现方式就有很多选择了。在 Java 平台上,可以使用 Proxy 模式,或者定制 ClassLoader 来实现 AOP 功能。在 .Net 平台上,要实现 AOP 的动态织入,归纳起来,可以采用以下几种方法:
l 使用 ContextAttribute 和 ContextBoundObject 来对对象的方法进行拦截。关于 ContextAttribute 的具体使用方法,读者可以参考 MSDN 等相关资料。
l 使用 Emit 来,在运行时刻动态构建被织入代码后的类,当程序调用被织入类时,实际上调用的是被修改后的类。 LOOM 使用的就是这种方式,但是,个人认为, LOOM 目前的实现非常生硬,其可扩展性和灵活性都不是很好。
l 使用 Proxy 模式。这也是本文将详细介绍的方法。
l 当然,在 ASP.Net 项目中,我们还有一种选择,就是使用 HTTPHandler 和 HTTPModule 来对自定义对 ASP.Net 页面的访问,加入一些我们需要的处理。关于如何使用 HTTPHandler 和 HTTPModule 的内容,可以参考笔者的文章 《 ASP.Net 中自定义 Http 处理及应用之 HttpHandler 篇》 ,以及 《 ASP.Net 中自定义 Http 处理及应用之 HttpModule 篇》
下面,我们来探讨如何使用 Proxy 模式,在 .Net 平台上实现一个可用的 AOP 框架。
** 第一个例子 **
首先,我们来看看 WebsharpAspect 的使用效果。我们可以使用以下步骤来完成我们的第一个例子的编写:
1、 在 VisualStudio 中新建一个控制台应用程序,把 Websharp.Aspect.dll 添加入引用。
2、 添加一个类,命名为 FirstAspect,并使他实现IAspect接口,添加代码如下:
public class FirstAspect : IAspect
{
public void Execute( object [] paramList)
{
Console.WriteLine("FirstAspect is called");
}
}
3、 添加一个 BusinessClass 类,模拟具体的业务逻辑类,使这个类继承 AspectObject 类,并添加 AspectManaged特性,然后添加两个方法,代码如下:
[AspectManaged( true )]
public class BusinessClass : AspectObject
{
public BusinessClass(){}
public void OutputMethod()
{
Console.WriteLine("OutputMethod()");
}
public void GetString()
{
Console.WriteLine("GetString()");
}
}
4、 为项目添加一个 App.config 配置文件,,并且添加以下内容:
< configuration >
< configSections >
< section name ="Websharp.Aspects" type ="Websharp.Aspect.AspectConfigHandler,Websharp.Aspect" /> < Websharp.Aspects >
< Aspect type ="WeaveTest.FirstAspect,WeaveTest" deploy-model ="Singleton"
pointcut-type ="Method|Construction" action-position ="Both" match ="," />
5、 在 Main 方法中添加如下代码:
public class MainClass
{
[STAThread]
static void Main ()
{
BusinessClass cls= new BusinessClass();
cls.OutputMethod();
cls.GetString();
Console.ReadLine();
}
}
运行以上代码,其结果如下:
可以看到, FirstAspect如我们所预期的那样,成功的拦截了BusinessClass的方法。
源代码和 全文见: http://www.websharp.org 也可以从 http://www.uml.org.cn/dvbbs6.0.0/index.asp 下载全部源代码