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的程序員在領導的要求下寫出了這樣變態的代碼。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM