在本文之前的前幾篇淺談.NET編譯時注入(C#-->IL)、淺談VS編譯自定義編譯任務—MSBuild Task(csproject)、編譯時MSIL注入--實踐Mono Cecil(1)已經討論了MSBuild和Mono.Cicel。在這裡我們將會利用它來實現一個簡單的編譯時AOP注入機制(這裡所說的編譯時是指c#到MSIL的預編譯過程)。我更傾向於像EL(微軟企業庫框架)這類動態AOP。編譯時AOP有PostSharp這種被稱之為靜態AOP框架,其優勢在於直接程式碼語句,效能更好,它不需要我們多餘的程式碼,像EL這種動態AOP,一般我們是不能直接new一個物件,需要容器(Container),在一些你的框架應用種,有時就需要使用者瞭解,再入我們對於WinForm、WebForm等.net平臺上主流的基於微軟事件機制的框架,事件方法的截獲,往往我們需要改變、包裝。在這時靜態AOP就顯出了他的優勢。
Class Diagram:
1:IMethodInject:Interface,擁有Executeing,Exceptioned,ExecuteSuccess三個契約為別為執行前,異常,成功。它們都有公同的引數型別:MethodExecutionEventArgs
Executeing:返回值為bool型別,將決定是否繼續執行方法體。Exceptioned:屬性Eeption代表發生的異常資訊,返回值ExceptionStrategy(取值:Handle, ReThrow, ThrowNew)決定異常處理機制,Handle已處理並忽略,ReThrow重新丟擲,ThrowNew丟擲一個包裝後的來源於MethodExecutionEventArgs 的Exception。ExecuteSuccess,對於擁有返回值的方法,可以修改MethodExecutionEventArgs 的ReturnValue,修改返回值。最後MethodExecutionEventArgs的Order決定多個Attribute的注入先後,即方法截獲的先後順序。
1:MethodInterceptBase:針對於方法Attribute標籤,實現方法截獲
public class MethodInterceptBase : Attribute, IMethodInject
{
public int Order
{
get;
set;
}
#region IMethodInject Members
public virtual bool Executeing(MethodExecutionEventArgs args)
{
return true;
}
public virtual ExceptionStrategy Exceptioned(MethodExecutionEventArgs args)
{
return ExceptionStrategy.ReThrow;
}
public virtual void ExecuteSuccess(MethodExecutionEventArgs args)
{
}
#endregion
}
2:MatchedMethodInterceptBase:和上面方法之上的MethodInterceptBase大體一致,區別在於其應用於class之上,屬性Rule為截獲方法匹配(應用於多個方法之上相同截獲),支援*匹配。
public class MatchedMethodInterceptBase : Attribute, IMethodInject
{
public int Order
{
get;
set;
}
public string Rule
{
get;
set;
}
#region IMethodInject Members
public virtual bool Executeing(MethodExecutionEventArgs args)
{
return true;
}
public virtual ExceptionStrategy Exceptioned(MethodExecutionEventArgs args)
{
return ExceptionStrategy.ReThrow;
}
public virtual void ExecuteSuccess(MethodExecutionEventArgs args)
{
}
#endregion
}
public class PropertyInterceptBase : Attribute, IMethodInject
{
public PropertyInterceptAction Action
{
get;
set;
}
public int Order
{
get;
set;
}
#region IMethodInject Members
public virtual bool Executeing(MethodExecutionEventArgs args)
{
return true;
}
public virtual ExceptionStrategy Exceptioned(MethodExecutionEventArgs args)
{
return ExceptionStrategy.ReThrow;
}
public virtual void ExecuteSuccess(MethodExecutionEventArgs args)
{
}
#endregion
}
其上預設都是Executeing繼續執行,Exceptioned為丟擲不處理,成功不修改result。
下面是一個簡單測試Code:
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Green.AOP;
namespace Test
{
// [TestAOP2Attribute(Rule = "TestMethod1*")]
public class Class1
{
// [TestAOPPropertyAttribute(Action = PropertyInterceptAction.Set)]
[TestAOPPropertyGetAttribute(Action = PropertyInterceptAction.Get)]
public testStrust TestProperty
{
get;
set;
}
[Obsolete()]
public static void Main(string[] args)
{
try
{
var y = new Class1();
// y.TestProperty = DateTime.Now;
Console.WriteLine(y.TestProperty);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
// new Class1().TestMethod1(1, 2, null);
Console.Read();
//throw new Exception("exfffffffffffffffffffff");
}
//[TestAOPAttribute(Order=1)]
//[TestAOP1Attribute(TestProperty = 1, Template = "sdsdsd",Order=0)]
public Class1 TestMethod1(int i, int j, Class1 c)
{
Console.WriteLine("ok");
return new Class1();
}
}
public class TestAOPPropertyGetAttribute : Green.AOP.PropertyInterceptBase
{
#region IMethodInject Members
public override bool Executeing(Green.AOP.MethodExecutionEventArgs args)
{
Console.WriteLine("------------------" + args);
Console.WriteLine(args.Instance);
Console.WriteLine(args.Method);
Console.WriteLine(this.GetType() + ":" + "Executeing");
return true;
}
public override Green.AOP.ExceptionStrategy Exceptioned(Green.AOP.MethodExecutionEventArgs args)
{
Console.WriteLine(this.GetType() + ":" + "Exceptioned");
return Green.AOP.ExceptionStrategy.ReThrow;
}
public override void ExecuteSuccess(Green.AOP.MethodExecutionEventArgs args)
{
Console.WriteLine("-----------");
Console.WriteLine(this.GetType() + ":" + "ExecuteSuccess" + "--result:" + args.ReturnValue);
}
#endregion
}
public class TestAOPPropertyAttribute : Green.AOP.PropertyInterceptBase
{
#region IMethodInject Members
public override bool Executeing(Green.AOP.MethodExecutionEventArgs args)
{
Console.WriteLine(args.Instance);
Console.WriteLine(args.Method);
Console.WriteLine(this.GetType() + ":" + "Executeing");
return true;
}
public override Green.AOP.ExceptionStrategy Exceptioned(Green.AOP.MethodExecutionEventArgs args)
{
Console.WriteLine(this.GetType() + ":" + "Exceptioned");
return Green.AOP.ExceptionStrategy.Handle;
}
public override void ExecuteSuccess(Green.AOP.MethodExecutionEventArgs args)
{
Console.WriteLine(this.GetType() + ":" + "ExecuteSuccess");
}
#endregion
}
public class TestAOP2Attribute : Green.AOP.MatchedMethodInterceptBase
{
#region IMethodInject Members
public override bool Executeing(Green.AOP.MethodExecutionEventArgs args)
{
Console.WriteLine(args.Instance);
Console.WriteLine(args.Method);
Console.WriteLine(this.GetType() + ":" + "Executeing");
return true;
}
public override Green.AOP.ExceptionStrategy Exceptioned(Green.AOP.MethodExecutionEventArgs args)
{
Console.WriteLine(this.GetType() + ":" + "Exceptioned");
return Green.AOP.ExceptionStrategy.Handle;
}
public override void ExecuteSuccess(Green.AOP.MethodExecutionEventArgs args)
{
Console.WriteLine(this.GetType() + ":" + "ExecuteSuccess");
}
#endregion
#region IMethodInject Members
public bool Match(System.Reflection.MethodBase method)
{
return true;
}
#endregion
}
//[AttributeUsage(AttributeTargets.Method)]
public class TestAOPAttribute : Green.AOP.MethodInterceptBase
{
#region IMethodInject Members
public override bool Executeing(Green.AOP.MethodExecutionEventArgs args)
{
Console.WriteLine(this.GetType() + ":" + "Executeing");
return true;
}
public override Green.AOP.ExceptionStrategy Exceptioned(Green.AOP.MethodExecutionEventArgs args)
{
Console.WriteLine(this.GetType() + ":" + "Exceptioned");
return Green.AOP.ExceptionStrategy.Handle;
}
public override void ExecuteSuccess(Green.AOP.MethodExecutionEventArgs args)
{
Console.WriteLine(this.GetType() + ":" + "ExecuteSuccess");
}
#endregion
#region IMethodInject Members
public bool Match(System.Reflection.MethodBase method)
{
return true;
}
#endregion
}
[AttributeUsage(AttributeTargets.Method)]
public class TestAOP1Attribute : Attribute, Green.AOP.IMethodInject
{
public int Order
{
get;
set;
}
public int TestProperty
{
get;
set;
}
public string Template
{
get;
set;
}
#region IMethodInject Members
public bool Executeing(Green.AOP.MethodExecutionEventArgs args)
{
Console.WriteLine(this.GetType() + ":" + "Executeing");
return true;
}
public Green.AOP.ExceptionStrategy Exceptioned(Green.AOP.MethodExecutionEventArgs args)
{
Console.WriteLine(this.GetType() + ":" + "Exceptioned");
return Green.AOP.ExceptionStrategy.Handle;
}
public void ExecuteSuccess(Green.AOP.MethodExecutionEventArgs args)
{
Console.WriteLine(this.GetType() + ":" + "ExecuteSuccess");
}
#endregion
#region IMethodInject Members
public bool Match(System.Reflection.MethodBase method)
{
return true;
}
#endregion
}
}
注意測試有兩種方式(由於沒有安裝包):
1:先重編譯測試專案,執行ConsoleApplication2(在屬性中修改控制檯其實引數)。在檢視測試專案。
2:將專案ConsoleApplication2修改為類庫,在新增修改csprojec資訊,Task位於Green.AOP.MyBuildTask,具體可以參見上一篇淺談VS編譯自定義編譯任務—MSBuild Task(csproject)。
在後續將會從簡單Demo分析實現原理。