PostSharp - Lightweight Aspect-Oriented System

[ 2007-04-06 17:41:13 | 作者: yuhen ]
字号: | |
PostSharp 是一个令人兴奋的项目,他结合了 MSBuild Task 和 MSIL Injection 技术,从另外一个角度实现 AOP 编程。试用过后你会感觉到其便利性,我们和以往基于 Dynamic Proxy 方式的 AOP 解决方案做个比较。
  • 由于采用 MSIL Injection,因此静态代码注入的执行效率要高于使用 Reflection Emit。
  • 使用 MSBuild Task,使得开发人员可以像使用编译器内置 Attribute 那样使用 AOP。
  • 可以拦截任意方法,而 Dynamic Proxy 方式的 AOP 往往采取继承方式来拦截 Virtual 方法。
  • 拥有更多的控制权。包括中断执行流程,修改参数和返回值等等。
  • 还可以拦截 Field Access、Exception 等操作。
  • 无需将对象创建代码改成 "new proxy()",更加透明。
  • 可以使用通配符进行多重拦截匹配。
  • 静态注入带来的问题更多的是注入代码的质量和调试复杂度。
我们写一个简单的例子,看看效果。
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using PostSharp.Laos;

[Serializable]
public class AopMethodAttribute : OnMethodBoundaryAspect
{
    public override void OnEntry(MethodExecutionEventArgs eventArgs)
    {
        // 获取方法名称
        Console.WriteLine("OnEntry:{0}.{1}", eventArgs.Method.ReflectedType, eventArgs.Method);
        
        // 显示方法参数
        ParameterInfo[] ps = eventArgs.Method.GetParameters();
        object[] pv = eventArgs.GetArguments();

        for (int i = 0; i < ps.Length; i++)
        {
            Console.WriteLine("  {0}={1}", ps[i].Name, pv[i]);
        }
    }

    public override void OnExit(MethodExecutionEventArgs eventArgs)
    {
        Console.WriteLine("OnExit...");
    }
}

public class MyClass
{
    [AopMethod]
    public int Test(int i)
    {
        Console.WriteLine("Test:{0}", i);
        return i++;
    }
}

MyClass o = new MyClass();
o.Test(123);

输出:
OnEntry:MyClass.Int32 Test(Int32)
i=123
Test:123
OnExit...

整个过程非常简单:
1. 创建一个继承自 OnMethodBoundaryAspect 的特性类,用于拦截方法。
2. override 特定的方法,执行拦截操作。方法参数提供了各种操作能力。
3. 向目标方法添加特性。
4. 编译,执行。

或许你会好奇,AopMethodAttribute 是如何被创建实例,并被执行的呢?

1. 注意编译时 VS2005 Output 窗口的输出内容,你会看到如下内容。其实 PostSharp 提供了一个 MSBuild Task,在我们每次编译时会调用它执行一些注入操作。
PostSharp 1.0 [1.0.4.162] - Copyright (c) Gael Fraiteur and Community, 2006.

2. 用反编译工具看看注入后的方法内容。看完这些代码,就算我不说,你也能明白它是如何实现拦截的了。
static MyClass()
{
    if (!~PostSharp~Laos~Implementation.initialized)
    {
        throw new LaosNotInitializedException();
    }
    ~PostSharp~Laos~Implementation.~targetMethod~1 = methodof(MyClass.Test);
    ~PostSharp~Laos~Implementation.~aspect~1.RuntimeInitialize(~PostSharp~Laos~Implementation.~targetMethod~1);
}

public int Test(int i)
{
    int returnValue;
    MethodExecutionEventArgs eventArgs;
    try
    {
        object[] arguments = new object[] { i };
        eventArgs = new MethodExecutionEventArgs(methodof(MyClass.Test, MyClass), this, arguments);
        ~PostSharp~Laos~Implementation.~aspect~1.OnEntry(eventArgs);
        if (eventArgs.FlowBehavior == FlowBehavior.Return)
        {
            return (int) eventArgs.ReturnValue;
        }
        Console.WriteLine("Test:{0}", i);
        int num = i++;
        returnValue = num;
        eventArgs.ReturnValue = returnValue;
        ~PostSharp~Laos~Implementation.~aspect~1.OnSuccess(eventArgs);
        returnValue = (int) eventArgs.ReturnValue;
    }
    catch (Exception exception)
    {
        eventArgs.Exception = exception;
        ~PostSharp~Laos~Implementation.~aspect~1.OnException(eventArgs);
        switch (eventArgs.FlowBehavior)
        {
            case FlowBehavior.Continue:
                return returnValue;

            case FlowBehavior.Return:
                return (int) eventArgs.ReturnValue;
        }
        throw;
    }
    finally
    {
        eventArgs.ReturnValue = returnValue;
        ~PostSharp~Laos~Implementation.~aspect~1.OnExit(eventArgs);
        returnValue = (int) eventArgs.ReturnValue;
    }
    return returnValue;
}

我们可以使用通配符拦截更多的内容,以简化我们的编码。
[AopMethod(AttributeTargetMembers="T*")]
public class MyClass
{
    public int T1(int i)
    {
        Console.WriteLine("T1:{0}", i);
        return i++;
    }

    public void T2(string s)
    {
        Console.WriteLine("T2:{0}", s);
    }
}

我们继续写一点其它的例子,诸如拦截属性访问和异常。

Field Aspect
[Serializable]
public class AopPropertyAttribute : OnFieldAccessAspect
{
    public override void OnGetValue(FieldAccessEventArgs eventArgs)
    {
        Console.WriteLine("ExposedFieldValue:{0}; StoredFieldValue:{1}", 
            eventArgs.ExposedFieldValue, eventArgs.StoredFieldValue);
    }

    public override void OnSetValue(FieldAccessEventArgs eventArgs)
    {
        Console.WriteLine("ExposedFieldValue:{0}; StoredFieldValue:{1}", 
            eventArgs.ExposedFieldValue, eventArgs.StoredFieldValue);
    }
}

public class MyClass
{
    [AopProperty]
    private int x;

    public int X
    {
        get { return x; }
        set { x = value; }
    }
}

Exception Aspect
[Serializable]
public class AopExceptionAttribute : OnExceptionAspect
{
    public override void OnException(MethodExecutionEventArgs eventArgs)
    {
        Console.WriteLine(eventArgs.Exception.Message);
        eventArgs.FlowBehavior = FlowBehavior.Return;
    }
}

public class MyClass
{
    [AopException]
    public void Test(int i)
    {
        throw new Exception("Error...");
    }
}

更详细的信息,请访问 PostSharp 网站,或者查阅其帮助文件。
[最后修改由 yuhen, 于 2007-04-06 21:55:52]
评论Feed 评论Feed: http://www.rainsts.net/feed.asp?q=comment&id=438

这篇日志没有评论。

发表评论
表情图标
[smile] [confused] [cool] [cry]
[eek] [angry] [wink] [sweat]
[lol] [stun] [razz] [redface]
[rolleyes] [sad] [yes] [no]
[heart] [star] [music] [idea]
UBB代码
转换链接
表情图标
悄悄话
用户名:   密码:  
验证码 * 请输入验证码