C#之AOP初探
来源:互联网 发布:淘宝助理怎么弄 编辑:程序博客网 时间:2024/06/08 15:39
一、前言
看了使用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初探
- C#之AOP初探
- spring源码学习之路---AOP初探
- AOP初探
- 初探aop
- 初探aop
- spring源码学习之路---AOP初探(六)
- spring源码学习之路---AOP初探(六)
- Spring初探之AOP(面向切面编程)
- 初探Spring AOP framework
- Spring AOP 初探
- Spring AOP 用法初探
- Spring AOP 初探
- Spring AOP 初探
- 初探Spring AOP
- C#初探之对窗体的操作
- 初探c#
- C#初探
- C# 初探
- 一元二次方程
- 第9周 项目6-2
- 斐波拉契算法
- 素材-IT发展简史
- Android学习笔记(二)之basic view
- C#之AOP初探
- linux静态库和动态库编译及使用
- 黑马训练营--IOS学习---OC语言学习总结2
- JfreeChart(1)——初探
- 问题解决——在虚拟机上测试串口软件 收到错误数据
- 游戏操作
- 【cf437D】The Child and Zoo
- Redis学习手册(Sorted-Sets数据类型)
- Android绘图机制(五)自定义控件的官方实例