定义函数A,B,C,调用A->B->C,这样就形成了函数静态调用链,而AOP要做的是能动态的添加多个B,形成A->B1->B2->B3...->C这样的效果,在EntLib(MS的企业库)Unity中有这样的实现,不过要看明白里面的代码的确需要花不少脑子,3年前看过里面的代码并做了记录,但是这两天翻出来看时照样化了很大精力,并杀死杀伤大量脑细胞,于是痛下决心将整个过程整理并画出时序图。
测试代码:
public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { BLLObj bll = new BLLObj(); InterceptionUtil util = new InterceptionUtil(); util.Pipeline.InterceptionBehaviors.Add(new BehaviorA() { Id = "1" }); util.Pipeline.InterceptionBehaviors.Add(new BehaviorA() { Id = "2" }); util.Pipeline.InterceptionBehaviors.Add(new BehaviorA() { Id = "3" }); var r = util.Call(bll, "Add", new object[] { 3, 4 }); Console.WriteLine("结果:" + r); Console.WriteLine(new string('-', 40)); var r1 = util.Call(bll, "Concat", new object[]{ new object[] { "a",1, "b", 2 }}); Console.WriteLine("结果:" + r1); Console.WriteLine(new string('-', 40)); var r2 = util.Call(bll, "DoSome", new object[] { }); Console.WriteLine("结果:" + r2); } } #region 测试业务类 public class BLLObj { public int Add(int a, int b) { Console.WriteLine("Add"); return a + b; } public string Concat(object[] args) { Console.WriteLine("Concat"); return string.Concat(args); } public void DoSome() { Console.WriteLine("DoSome"); } } #endregion
(图1)函数链调用的效果图:
从图1可以看到,在目标对象的目标方法被调用前包裹了3层拦截类的函数调用,正是这样嵌套结构允许我们实现数据库事务控制,错误记录,权限控制,性能监测等一系列AOP应用.
实现代码
下面的代码涉及2个接口与5个类,话说人脑在同时考虑多个对象时会很受伤,所以俺在代码里加了必要的注释并在下面付上了调用序列图,请配合起来看。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; namespace FuncLinkT2 { //两个互相引用的接口,从定义的这一刻开始 //就注定了他们需要互相依靠,相伴一身....... //当然哥们的纠结也从此开始 public interface IInterceptionBehavior { MethodReturn Invoke(MethodInvocation input, IGetNextInterceptionBehavior nb); } public interface IGetNextInterceptionBehavior { IInterceptionBehavior GetNext(); } /// <summary> /// 作为IInterceptionBehavior.Invoke参数, /// 便于拦截行为类获取当前拦截方法的信息 /// 包括被拦截对象与被拦截方法 /// </summary> public class MethodInvocation { public MethodBase MethodBase { get; set; } public object Target { get; set; } } /// <summary> /// 被拦截方法的返回结果包装 /// 同样便于拦截行为类获取方法调用后接返回结果 /// </summary> public class MethodReturn { public object ReturnValue { get; set; } public Exception Err { get; set; } } #region 一个拦截行为类的实现 public class BehaviorA : IInterceptionBehavior { public string Id { get; set; } public MethodReturn Invoke(MethodInvocation input, IGetNextInterceptionBehavior nb) { Console.WriteLine("Behavior:" + Id + "->Invoke Befor!"); IInterceptionBehavior behavior = nb.GetNext(); var r = behavior.Invoke(input, nb); Console.WriteLine("Behavior:" + Id + "-> invoke After!"); return r; } } #endregion /// <summary> /// 实际对象调用代理类 /// 包装成IInterceptionBehavior以便加到BehaviorPipeline末尾 /// </summary> public sealed class InterceptionInvocationProxy : IInterceptionBehavior { public object[] Args; public MethodInvocation Invocation; public object Target; public MethodReturn Invoke(MethodInvocation input, IGetNextInterceptionBehavior nb) { Console.WriteLine("InterceptionInvocationProxy->实际对象将被调用!"); var ret= new MethodReturn(); try { ret.ReturnValue = Invocation.MethodBase.Invoke(Target, Args); } catch (Exception ex) { ret.Err = ex; } return ret; } } public sealed class NextBehaviorsImp : IGetNextInterceptionBehavior { internal InterceptionBehaviorPipeline Pipeline; internal int InterceptorIndex; internal IInterceptionBehavior Target; public IInterceptionBehavior GetNext() { InterceptorIndex++; if (InterceptorIndex < Pipeline.InterceptionBehaviors.Count) { IInterceptionBehavior local1 = Pipeline.InterceptionBehaviors[InterceptorIndex]; return local1; } return Target; } } /// <summary> /// 拦截行为链条 /// </summary> public class InterceptionBehaviorPipeline { internal readonly List<IInterceptionBehavior> InterceptionBehaviors; // Properties public int Count { get { return InterceptionBehaviors.Count; } } // Methods public InterceptionBehaviorPipeline() { InterceptionBehaviors = new List<IInterceptionBehavior>(); } public MethodReturn Call(MethodInvocation input, IInterceptionBehavior target) { //无任何拦截行为 if (InterceptionBehaviors.Count == 0) { return target.Invoke(input, null); } //至少有一个IInterceptionBehavior NextBehaviorsImp nb = new NextBehaviorsImp(); nb.Target = target; nb.Pipeline = this; nb.InterceptorIndex = 0; return InterceptionBehaviors[0].Invoke(input, nb); } } public class InterceptionUtil { internal InterceptionBehaviorPipeline Pipeline = new InterceptionBehaviorPipeline(); public object Call(BLLObj targetObj, string methodName, object[] args) { InterceptionInvocationProxy proxy = new InterceptionInvocationProxy(); proxy.Target = targetObj; proxy.Args = args; proxy.Invocation = new MethodInvocation(); proxy.Invocation.MethodBase = proxy.Target.GetType().GetMethod(methodName); proxy.Invocation.Target = proxy.Target; //将目标类的方法封装代理提供给调用连,开始进行拦截调用 MethodReturn ret= Pipeline.Call(proxy.Invocation, proxy); return ret.ReturnValue; } } }
(图2)调用序列--务必鼠标右键找到图片url后看大图
参考
1.当然实现函数调用链不一定要搞这么麻烦,可以通过嵌套代理类来实现上面的调用效果.
地址:http://www.cnblogs.com/wdfrog/p/3162524.html
2.如果哥们觉得上面的看起来很轻松,那么你可以直接看MS使用委托代替上面两个接口的实现代码.
地址:http://www.cnblogs.com/wdfrog/archive/2010/10/23/1859021.html
至于为什么微软要用委托(而且用了大量的匿名委托)来实现,估计是觉得用接口是java干的事情,为了跟其保持距离所以某个MS的程序员在领导的要求下写出了这样变态的代码。