65.9K
CodeProject 正在变化。 阅读更多。
Home

面向方面编程 101

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.88/5 (7投票s)

2010年1月4日

CPOL

4分钟阅读

viewsIcon

36307

downloadIcon

230

什么是面向方面编程?在哪里使用它?

引言

本文讨论面向方面编程的基础知识。这是一篇 101 级别的讨论。在本文中,我们将描述 AOP 的基本概念及其在分层架构环境中的必要性。最后,我们将使用 AOP 开发的 Enterprise library 详细介绍一个日志框架。

目标读者

本文的主要读者是具有分层架构及其优势的理解的架构师和设计师。

分层架构和关注点分离

下图显示了一个典型的 .NET Web 应用程序的分层架构。

它告诉我们有很多层。每一层都有一个目的、需求、契约,并且是其下方和上方层的屏障。

数据访问层负责如何存储和检索数据。它的需求是访问数据,它由数据访问方法定义了契约,它屏蔽了应用程序免受数据库更改的影响,也屏蔽了数据库免受业务规则的影响。通常,数据访问层甚至不会意识到 UI 的工作方式。

无法分离的关注点该怎么办?

存在大量无法分离的关注点。这些是横切关注点。这些的更改意味着整个应用程序的更改。

当我们编写代码访问数据库时,我们只在数据访问层编写代码;然而,编写跟踪日志的代码却分布在整个应用程序中。如果出于某种原因,我们一直只跟踪方法名称,有一天计划开始跟踪方法的输入参数和返回类型,那么就必须在整个应用程序中进行代码更改。

不幸的是,横切关注点往往是应用程序中考虑最少的方面,想想我们在设计中有多少次考虑过异常层次结构?

什么是 AOP

AOP 或面向方面编程是一种机制,通过该机制可以单独开发横切关注点,然后将其与不同层的执行代码结合起来。显而易见,我们可以有两种类型的 AOP 框架,一种在运行时挂接到代码,另一种在设计时修改代码。

我觉得“面向方面编程”这个词有点令人困惑,我宁愿称之为“纠缠的关注点分离模式”。

.NET 中一些不错的 AOP 框架

存在大量的 AOP 框架。在本次讨论中,我们将使用 PostSharp (http://www.postsharp.org/aop-net/overview)。该框架在编译时修改程序集。

PostSharp 是一个非常详细且组织良好的框架,一篇文章不足以对其进行充分的介绍。在本文中,我们将使用 PostSharp 和 Microsoft Enterprise Library 构建的日志框架来探讨一些非常基础的示例。

PostSharp 允许修改类的方面。让我们看一下 OnMethodBoundaryAspect 类,它可以被最终用户覆盖。此类提供了各种钩子:

  • OnEntry - 进入方法时调用
  • OnExit - 方法退出时调用
  • OnException - 方法抛出异常时调用

等等……

我们可以覆盖这些方法并在其中进行日志记录。

class OnMyExceptionAspect : OnMethodBoundaryAspect
{
}

我们可以覆盖 OnEntry

public override void OnEntry(MethodExecutionEventArgs eventArgs)
{
base.OnEntry(eventArgs);
Console.WriteLine(String.Format("Entering method {0}", eventArgs.Method.Name));
object[] methodArguments = eventArgs.GetReadOnlyArgumentArray();
foreach (object methodArgument in methodArguments)
{
Console.WriteLine(String.Format("The method argument is {0}", methodArgument));
}
}

正如我们在示例代码中看到的,使用 MethodExecutionEventArgs 类来查找方法的详细信息并将详细信息写入控制台。您可以在 AOPPostSharp_SampleCodeForConsoleLogging.zip 中找到此代码。

您需要为该示例安装 PostSharp 和 Visual Studio 2005。

挂接到类-如何编译?

为了在编译期间将此信息写入 DLL,我们必须修改程序集信息和 Microsoft 生成文件。

程序集信息通过输入进行修改

[assembly: AOPSample.OnMyExceptionAspect(AttributeTargetAssemblies = 
	"AOPPostSharp", AttributeTargetTypes = "AOPSample.Program")]

这将告诉将使用哪个 AOP 类。

所有魔力都发生在编译过程中,它会将一些信息添加到 .csproj 文件中。

<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="C:\Program Files\PostSharp 1.5\PostSharp-1.5.targets" />

这本质上是一个 Microsoft 生成文件,我们在其中添加了有关如何进行编译的一些信息。

查看原始代码和反编译代码

编译前的代码片段是:
static void Main(string[] args)
{
MethodWithNoException(10, 5);
try
{
And after compilation on decompiling with reflector 
MethodExecutionEventArgs ~laosEventArgs~2; 
try 
{ 
object[] ~arguments~1 = new object[] { args }; 
~laosEventArgs~2 = new MethodExecutionEventArgs
	(<>AspectsImplementationDetails_1.~targetMethod~4, null, ~arguments~1); 
<>AspectsImplementationDetails_1.AOPSample.OnMyExceptionAspect~4.
	OnEntry(~laosEventArgs~2); 
if (~laosEventArgs~2.FlowBehavior != FlowBehavior.Return) 
{ 
MethodWithNoException(10, 5); 
try 
{

执行代码后,我们看到

这是很多我们没有添加的跟踪信息,它是在编译过程中添加的。

一个非常简单的日志框架示例

利用上述概念,我们将尝试开发一个简单的日志记录器,它将使用 AOP 使用 Enterprise Library 记录信息。

在本次讨论中,我们将假定读者熟悉 Microsoft Enterprise Library。

日志框架

public override void OnEntry(MethodExecutionEventArgs eventArgs)
{
base.OnEntry(eventArgs);
StringBuilder message = new StringBuilder(String.Format
	("Entering method {0}", eventArgs.Method.Name));
object[] methodArguments = eventArgs.GetReadOnlyArgumentArray();
foreach (object methodArgument in methodArguments)
{
message.Append(String.Format("The method argument is {0}", methodArgument));
}
LogDebug(message);
}
private static void LogDebug(string message)
{
LogEntry logEntry = new LogEntry();
logEntry.EventId = 100;
logEntry.Priority = 2;
logEntry.Message = message;
logEntry.Categories.Add("Trace");
Logger.Write(logEntry);
}

正如您所见,代码中我们没有做什么特别巧妙的事情,所有好的部分都已经被 AOP 框架完成了,我们所做的只是写了几行代码来使用一个非常好的框架进行日志记录。

这不是一个非常出色的框架,甚至糟糕到包含硬编码,但它体现了我们的想法。

我打算在几天内提出一个具有得体设计的完整异常处理框架,以详细说明这个工具能有多好。

历史

  • 2010 年 1 月 4 日:初始发布
© . All rights reserved.