C#之AOP初探

一、前言

看了使用AOP 使C#代码更清晰,很受启发。但版主似乎已经放弃C#了,所以他的这篇文章让人看得似懂非懂。但文中一段话说的特好:

有许多AOP类库通过使用编译事件和IL操作允许你在编译时“处理”这些方面,例如PostSharp;而某些类库使用DynamicProxy在运行时处理;某些要求你的类继承自ContextBoundObject使用C#内建特性来supportAspects。所有的这些都有某些“不便”。你不得不使用某些外部库,做足够的性能测试来那些类库可扩展等等。而你需要的只是一个非常简单的方式来实现“隔离”,可能并不是想要完全实现AOP。记住,你的目的是隔离那些并不重要的核心代码,来让一切变得简单并且清晰!

学AOP。首先要弄清楚什么是AOP,恐怕网上无数的文章只能让我们不知所以,每篇文章要不是直接代码,就是眉毛胡子摘抄一大篇。我花了几个晚上,在网上另外找了一篇终于能让人理解什么是AOP的文章:颠覆C#王权的“魔比斯环” — 实现AOP框架的终极利器,但这篇文章没有深入讲解Emit实现,尽在最后呈现了一份Emit实现AOP的成果,让人欲罢不能。好了,啰嗦了一大篇,我想用几句话谈谈我对AOP的理解:

1 了解来历:AOP(Aspect Oriented Programming)的中文意思是“面向方面编程”。由施乐公司提出,是OOP的一种扩展。

2 弄清楚表现形式:代码侵入,他有一个新词叫织入,是AOP最重要最明显的特性。所谓织入:就是对原生方法进行拦截(这其中也包括构造方法),在执行原方法时,要在两个位置来执行我们织入的代码,如日志代码。这两个位置是before(在原代码执行之前)和after(在原代码执行之后)。例如:

原生代码:

public class A
{
  private int _i=0;
  public int Add(int i)
  {
	return i+=i;
  }
}

AOP织入代码后会变成类似如下形式:

public class A
{
  private int _i=0;
  public int Add(int i)
  {
        int result=_i
        Log("Add begin");
	result+=i;
<pre name="code" class="csharp">        Log("Add end");
return result; }}

当然,还可以对源代码进行around(完全取代原来的代码)。

3 搞清楚用途:有点像黑客!目的已经很明显,但这么做的意义是什么呢?使用AOP技术,将一些与业务无关的系统功能完全独立成单独的模块,在必要时可以简捷方便的使用,比如比如安全性检查、日志记录、性能监控,异常处理等,一方面维护了OOP的“单一职责”原则,降低模块之间的耦合度,另一方面是这些处理透明化,达到简洁代码,直接提高可读性,间接提高可维护性,提高工作效率的目的。

4 知道实现方法:包括静态织入和动态织入,静态织入是在程序编译时完成的,动态织入是在运行期间完成的。

5 理解与OOP/OOD的关系:有人一句话总结:OOD/OOP面向名词领域,AOP面向动词领域。 

6 应该了解的一些基本概念: 

       。联结点(JointPoint) :一个联结程序执行过程中的一个特定点。典型的联结点有:调用一个方法;方法执行这个过程本身;类初始化;对象初始化等。联结点是 AOP 的核心概念之一,它用来定义在程序的哪里通过 AOP 加入新的逻辑。

       。切入点(Pointcut) :一个切入点是用来定义某一个通知该何时执行的一组联结点。通过定义切入点,我们可以精确地控制程序中什么组件接到什么通知。上面我们提到,一个典型的联结点是方法调用,而一个典型的切入点就是对某一个类的所在方法调用的集合。通常我们会通过组建复杂的切入点来控制通知什么时候被执行。        。通知(Advice) :在某一个特定的联结点处运行的代码称为“通知”。通知有很多种,比如在联结点之前执行的前置通知(before advice)和在联结点之后执行的后置通知(after advice) 。

        。方面(Aspect) :通知和切入点的组合叫做方面,所以,方面定义了一段程序中应该包括的逻辑,以及何时应该执行该逻辑。

        。织入(Weaving) :织入是将方面真正加入程序代码的过程。对于静态 AOP 方案而言,织入是在编译时完成的,通常是在编译过程中增加一个步骤。类似的,动态 AOP 方案则是在程序运行是动态织入的。

        。目标(Target) :如果一个对象的执行过程受到某一个 AOP 的修改,那么它就叫一个目标对象。目标对象通常也称为被通知对象。

        。引入(Introduction) :   通过引入,可以在一个对象中加入新的方法或属性,以改变它的结构,这样即使该对象的类没有实现某一个接口,也可以修改它,使之成为该接口的一个实现。   

7 C#中的AOP思想:典型的,Attribute就是一种AOP思想的简单实现,但使用很繁琐,而且反射机制总是让人感觉有很大的性能开销。其他的,通过Emit织入是似乎是完美的做法,但太复杂,一般人(至少我)弄不来。二那些需要指定继承某些类来完成AOP的做法更是一眼便被深恶痛绝(谁希望随时享受者被强奸的感觉?)。因此,我非常喜欢使用AOP 使C#代码更清晰一文提到的AOP思想,但博主仅仅实现了前置织入。本文随后我将对其进一步完善,完成后置织入的处理。完成后的代码基本上已经可以投入实用了。

二、代码实现

1 核心代码部分:AspectF.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Diagnostics;

namespace aoptest {

    /// <summary>
    /// AspectF,AOP基础类
    /// </summary>
    public class AspectF
    {
        /// <summary>
        /// 获取AspectF实例
        /// </summary>
        public static AspectF Define
        {
            [DebuggerStepThrough]
            get
            {
                return new AspectF();
            }
        }

         /// <summary>
        /// Action执行的出错信息
        /// </summary>
        /// <typeparam name="T"></typeparam>
        [DebuggerStepThrough]
        public class ActionError<T> : Chiefan.Common.ActionResult<T> { }

        /// <summary>
        /// Action重试过程中的出错处理委托
        /// </summary>
        /// <param name="e"></param>
        /// <param name="cancel"></param>
        public delegate void RetryErrorHandle(Exception e, ref bool cancel);

       /// <summary>
        /// DO的执行结果状态
        /// </summary>
        public ActionError<Exception> Error { get { return error; } }
        
        /// <summary>
        /// 是否启用计算并打印执行耗时
        /// </summary>
        public bool AllowHowLong { get; set; }

        /// <summary>
        /// 执行一个没有返回值的workItem
        /// </summary>
        /// <param name="work">action</param>
        [DebuggerStepThrough]
        public void Do(Action work)
        {
            System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace();
            System.Diagnostics.StackFrame sf = stackTrace.GetFrame(1);//[0]为本身的方法 [1]为调用方法
            string abc= string.Format("{0}.{1}", sf.GetMethod().ReflectedType.FullName, sf.GetMethod().Name);//类名

            try
            {
                if (this.Chain == null)
                {
                    this.WriteLog(Logger.LogType.Debug, "方法[{0}]执行时间计时开始", abc);
                    int beginTime = Environment.TickCount;
                    work();
                    this.WriteLog(Logger.LogType.Debug, "方法[{0}]执行时间共计{1}毫秒", abc, Environment.TickCount - beginTime);
                }
                else
                {
                    this.Chain(() => {
                        this.WriteLog(Logger.LogType.Debug, "方法[{0}]执行时间计时开始", abc);
                        int beginTime = Environment.TickCount;
                        work();
                        this.WriteLog(Logger.LogType.Debug, "方法[{0}]执行时间共计{1}毫秒", abc, Environment.TickCount - beginTime);
                    });
                }
            }
            catch (Exception ex)
            {
                error.ErrorCode = -1;
                error.ErrorMessage = "未知错误";
                error.Data = ex;

                this.WriteLog(Logger.LogType.Error, "{0} - {1}", abc, ex.Message);
            }
            if (this.PostChain != null)
                this.PostChain(() => { });
        }

        /// <summary>
        /// 绑定一个新的委托
        /// </summary>
        /// <param name="newAspectDelegate">委托</param>
        /// <returns>返回AspectF实例</returns>
        [DebuggerStepThrough]
        public AspectF Combine(Action<Action> newAspectDelegate, bool isPost=false)
        {
            if (isPost)
            {
                if (this.PostChain == null)
                {
                    this.PostChain = newAspectDelegate;
                }
                else
                {
                    Action<Action> existingChain = this.PostChain;
                    Action<Action> callAnother = (work) =>
                        existingChain(() => newAspectDelegate(work));
                    this.PostChain = callAnother;
                }
            }
            else
            {
                if (this.Chain == null)
                {
                    this.Chain = newAspectDelegate;
                }
                else
                {
                    Action<Action> existingChain = this.Chain;
                    Action<Action> callAnother = (work) =>
                        existingChain(() => newAspectDelegate(work));
                    this.Chain = callAnother;
                }
            }

            return this;
        }

        /// <summary>
        /// 打印日志
        /// </summary>
        /// <param name="logType"></param>
        /// <param name="format"></param>
        /// <param name="args"></param>
        [DebuggerStepThrough]
        public void WriteLog(Logger.LogType logType, string format, params object[] args)
        {
            switch (logType)
            {
                case Logger.LogType.Error:
                    log.Error(format, args);
                    break;
                case Logger.LogType.Debug:
                    log.Debug(format, args);
                    break;
                case Logger.LogType.Warn:
                    log.Warn(format, args);
                    break;
                case Logger.LogType.Fatal:
                    log.Fatal(format, args);
                    break;
                case Logger.LogType.Info:
                default:
                    log.Info(format, args);
                    break;
            }

        }

        Action<Action> Chain = null;
        Action<Action> PostChain = null;
        Logger.ILog log = Logger.Log.Instance;
        ActionError<Exception> error = new ActionError<Exception>();

    }

}

2 扩展代码部分

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace aoptest
{
    using Logger = Chiefan.eSail2.Logger;

    /// <summary>
    /// AspectF扩展类
    /// </summary>
    public static class AspectExt
    {
        /// <summary>
        /// 记录日志
        /// </summary>
        /// <param name="aspect"></param>
        /// <param name="logType">日志类型:error|debug|info</param>
        /// <param name="message"></param>
        /// <param name="isPost">是否后置执行</param>
        /// <returns></returns>
        [DebuggerStepThrough]
        public static AspectF Log(this AspectF aspect, Logger.LogType logType, string message, bool isPost = false)
        {
            return aspect.Combine((work) =>
            {
                aspect.WriteLog(logType, "{0}", message);
                work();
            }, isPost);
        }

        /// <summary>
        /// 性能监视
        /// </summary>
        /// <param name="aspects"></param>
        /// <returns></returns>
        [DebuggerStepThrough]
        public static AspectF HowLong(this AspectF aspects)
        {
            aspects.AllowHowLong = true;
            return aspects;
        }

        /// <summary>
        /// Do的失败后重复执行版本
        /// </summary>
        /// <param name="aspects"></param>
        /// <param name="errorHandle">错误处理句柄</param>
        /// <param name="failedHandle">失败处理句柄</param>
        /// <param name="retryDuration">重试间隔(毫秒)</param>
        /// <param name="retryCount">重试次数</param>
        /// <returns></returns>
        [DebuggerStepThrough]
        public static AspectF Retry(this AspectF aspects,
            AspectF.RetryErrorHandle errorHandle, Action failedHandle, 
            int retryCount = 1, int retryDuration = 1000)
        {
            return aspects.Combine((work) =>
                Retry(retryDuration, retryCount, errorHandle, failedHandle, work));
        }

        /// <summary>
        /// Retry
        /// </summary>
        /// <param name="retryDuration">retry的时间间隔</param>
        /// <param name="retryCount">retry次数</param>
        /// <param name="errorHandler">错误处理句柄</param>
        /// <param name="retryFailed">retry失败处理句柄</param>
        /// <param name="work">真实的委托内容</param>
        [DebuggerStepThrough]
        public static void Retry(int retryDuration, int retryCount,
            AspectF.RetryErrorHandle errorHandler, Action retryFailed, Action work)
        {
            bool cancel = false;
            bool success = false;
            do
            {
                try
                {
                    success = true;
                    work();
                }
                catch (Exception ex)
                {
                    errorHandler(ex, ref cancel);
                    System.Threading.Thread.Sleep(retryDuration);
                }
            } while (retryCount-- > 0);
            if (!success)
                retryFailed();
        }


        [DebuggerStepThrough]
        public static AspectF Delay(this AspectF aspect, int milliseconds)
        {
            return aspect.Combine((work) =>
            {
                System.Threading.Thread.Sleep(milliseconds);
                work();
            });
        }

        [DebuggerStepThrough]
        public static AspectF MustBeNonNull(this AspectF aspect, Action<AspectF> errorHandle, params KeyValuePair<string,object>[] args)
        {
            return aspect.Combine((work) =>
            {
                for (int i = 0; i < args.Length; i++)
                {
                    object arg = args[i].Value;
                    if (arg == null)
                    {
                        string errorMessage = string.Format("{0}, Action被终止.", args[i].Key);
                        aspect.Error.ErrorCode = -1;
                        aspect.Error.ErrorMessage = errorMessage;
                        aspect.Error.Data = new ArgumentException(errorMessage);

                        System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace();
                        System.Diagnostics.StackFrame sf = stackTrace.GetFrame(1);//[0]为本身的方法 [1]为调用方法
                        string abc = string.Format("{0}.{1}", sf.GetMethod().ReflectedType.FullName, sf.GetMethod().Name);//类名
                        aspect.WriteLog(Logger.LogType.Error, "{0} - {1}", abc, errorMessage);

                        errorHandle(aspect);
                    }
                }
                work();
            });
        }

        public static AspectF MustBeNonDefault<T>(this AspectF aspect, Action<AspectF> errorHandle, params KeyValuePair<string,T>[] args) where T : IComparable
        {
            return aspect.Combine((work) =>
            {
                T defaultvalue = default(T);
                for (int i = 0; i < args.Length; i++)
                {
                    T arg = args[i].Value;
                    if (arg == null || arg.Equals(defaultvalue))
                    {
                        //throw new ArgumentException(string.Format("Parameter at index {0} is null", i));
                        string errorMessage = string.Format("{0}, Action被终止.", args[i].Key);
                        aspect.Error.ErrorCode = -1;
                        aspect.Error.ErrorMessage = errorMessage;
                        aspect.Error.Data = new ArgumentException(errorMessage);

                        System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace();
                        System.Diagnostics.StackFrame sf = stackTrace.GetFrame(1);//[0]为本身的方法 [1]为调用方法
                        string abc = string.Format("{0}.{1}", sf.GetMethod().ReflectedType.FullName, sf.GetMethod().Name);//类名
                        aspect.WriteLog(Logger.LogType.Error, "{0} - {1}", abc, errorMessage);
                        errorHandle(aspect);
                    }
                }
                work();
            });
        }

        public static AspectF WhenTrue(this AspectF aspect, params Func<bool>[] conditions)
        {
            return aspect.Combine((work) =>
            {
                int i = 0;
                foreach (Func<bool> condition in conditions)
                {
                    if (!condition())
                    {
                        string errorMessage = string.Format("执行条件[{0}]不满足要求,Action被终止.", i);
                        aspect.Error.ErrorCode = -1;
                        aspect.Error.ErrorMessage = errorMessage;
                        aspect.Error.Data = new ArgumentException(errorMessage);

                        System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace();
                        System.Diagnostics.StackFrame sf = stackTrace.GetFrame(1);//[0]为本身的方法 [1]为调用方法
                        string abc = string.Format("{0}.{1}", sf.GetMethod().ReflectedType.FullName, sf.GetMethod().Name);//类名
                        aspect.WriteLog(Logger.LogType.Error, "{0} - {1}", abc, errorMessage);
                        return;
                    }
                    i++;
                }
                work();
            });
        }

    }
}
3 使用范例:

        public static void InsertCustomerTheEasyWay(string firstName, string lastName, int age,
            Dictionary<string, string> attributes)
        {
            AspectF.Define
                .Log(Logger.LogType.Debug, "Inserting customer the easy way1")
                .Log(Logger.LogType.Debug, "Inserting customer the easy way2")
                .Log(Logger.LogType.Debug, "Inserting customer the ooo2 way", true)
                .Log(Logger.LogType.Debug, "Inserting customer the ooo1 way", true)
                .HowLong()
                .Do(() => { Console.WriteLine("ok!"); })
                //.Do(() => {throw new Exception("a dog is at ahead!"); })
                //.Do<string>(new Func<string>(() => { Console.WriteLine("ok"); return "okey!"; }));
                ;
        }
准备在项目上试用一下,试用成功后我将继续更新本文。

欢迎共同探讨,转载请保留本文连接:C#之AOP初探

展开阅读全文

没有更多推荐了,返回首页