• AOP之拦截函数调用链实现


    定义函数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
    View Code

    (图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;
    
            }
        }
    
    
    
    
    
    
    
    
    
    
    }
    View Code

    (图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的程序员在领导的要求下写出了这样变态的代码。

  • 相关阅读:
    工厂设计模式
    二分查找(java实现)
    集合
    JS中,根据div数值判断弹出窗口
    用JS,打印正立三角形
    用JS,打印99乘法表
    用JS,求斐波那契数列第n项的值
    用JS 循环做一个表格
    JS中,如何判断一个被转换的数是否是NaN
    用JS写一个简单的程序,算出100中7的倍数的最大值
  • 原文地址:https://www.cnblogs.com/wdfrog/p/3166828.html
Copyright © 2020-2023  润新知