1.上下文Context、面向切面编程AOP模型分析
在本人的.NET面向上下文、AOP架构模式(概述)一文中,我们大概了解了上下文如何辅助对象在运行时的管理。在很多时候我们急需在运行时能把对象控制在一定的逻辑范围内,在必要的时候能让他们体现出集中化的概念,如人群、车辆、动物等等。而Context与AOP有着密切的联系,Context表示逻辑抽象的范围而AOP描述了在这个逻辑范围内如何进行控制。其实这两者都是设计模式外的设计模式,与具体的技术实现无关。[王清培版权所有,转载请给出署名]
那么Context与AOP两者在逻辑上是一个怎样的概念呢?似乎只有图才能最贴切的表达人的理解思路。下图展现Context与AOP紧密合作的概念模型。
对象在运行时被上下文管理,在上下文中可以很方便的获取到所有的受管理的对象,这为后面的AOP做了铺垫。只有Context启动后AOP管理器的爪子才能伸进对象的运行时内部与AOP的连接点建立起通讯关系,才能真正的使对象能面向切面成功。[王清培版权所有,转载请给出署名]
通过合理的约定对象的AOP抽象接口,尽可能的最大化将控制权移动到客户所实现的“面”中去。比如对某类方法的调用,可能需要尽可能的控制方法的所有执行权。所以对具体的抽象定义有很大的难度。
/*** * author:深度训练 * blog:http://wangqingpei557.blog.51cto.com/ * **/ using System; using System.Collections.Generic; using System.Text; namespace ContextModule { ////// 上下文运行时环境。 /// 上下文逻辑运行时环境,环境中的功能都是可以通过附加进来的。 /// public class ContextRuntime : IDisposable { #region IDisposable成员 void IDisposable.Dispose() { _currentContextRuntime = null; } #endregion protected ContextRuntime() { } private DateTime _initTime = DateTime.Now; /// /// 获取运行时创建上下文的时间 /// public virtual DateTime InitTime { get { return _initTime; } } private Dictionary<object, object> _runTimeResource = new Dictionary<object, object>(); private ContextFilterHandlerMap _filterMap = new ContextFilterHandlerMap(); /// /// 获取上下文中的方法、类过滤器映射表 /// public ContextFilterHandlerMap FilterMap { get { return _filterMap; } } private Guid _initPrimaryKey = Guid.NewGuid(); /// /// 获取运行时创建上下文的唯一标识 /// public virtual Guid InitPrimaryKey { get { return _initPrimaryKey; } } /// /// 获取上下文共享区域中的数据 /// /// 数据Key /// object数据对象 public virtual object GetValue(object key) { return _runTimeResource[key]; } /// /// 设置上下文共享区域中的数据 /// /// 数据Key /// 要设置的数据对象 public virtual void SetValue(object key, object value) { _runTimeResource[key] = value; } [ThreadStatic] private static ContextRuntime _currentContextRuntime; /// /// 获取当前上下文运行时对象. /// public static ContextRuntime CurrentContextRuntime { get { return _currentContextRuntime; } } /// /// 开始运行时上下文 /// /// ContextRuntime public static ContextRuntime BeginContextRuntime() { //可以通过配置文件配置上下文运行时环境的参数。这里只是实现简单的模拟。 _currentContextRuntime = new ContextRuntime(); return _currentContextRuntime; } } }
这里只为了实现基本的模型原型,不会涉及太多的功能。上下文主要是在当前线程中开启,然后保持在静态对象的多线程安全访问,最后就是对象的稳定释放。
2.2上下文对象绑定实现
有了上下文之后,如何才能使对象在运行时动态的绑定到上下文中来。这个需要在前期编码的时候就确定对象是否要绑定到当前上下文以便进行管理。
/*** * author:深度训练 * blog:http://wangqingpei557.blog.51cto.com/ * **/ using System; using System.Collections.Generic; using System.Text; using System.Reflection; namespace ContextModule { ////// 上下绑定基类,强制派生类绑定到上下文。 /// 逻辑上下文的策略构造都在这里进行。 /// /// 受管理的上下文绑定对象类型,通常是ContextModuleBaseObject派生类。 public class ContextModuleBaseObject : ContextRuntime where T : class { /// /// 当前上下文绑定对象所绑定到的上下文物理对象实例。 /// private ContextRuntime _contextRunTime; public ContextModuleBaseObject() { if (typeof(T).GetCustomAttributes(typeof(ContextEveningBoundAttribute), false) != null) { _IsEvening = true; return; } //前期静态绑定上下文 if (ContextRuntime.CurrentContextRuntime == null) throw new Exception("上下文环境未能初始化,请检查您的代码入口是否启用了ContextRuntime对象。"); _contextRunTime = ContextRuntime.CurrentContextRuntime; _InitContextHandler (); } /// /// 构造上下文的类过滤器、方法过滤器映射表。 /// private void _InitContextHandler () where ChildType : class { //构造类过滤器 ContextOperationBaseAttribute[] classattr = typeof(ChildType).GetCustomAttributes(typeof(ContextOperationBaseAttribute), false) as ContextOperationBaseAttribute[]; if (classattr.Length > 0) { ContextOperationBaseAttribute joinoper = _JoinOperation(classattr); _contextRunTime.FilterMap.MapOperation(typeof(T).FullName, joinoper); } //构造方法过滤器 foreach (MethodInfo method in typeof(ChildType).GetMethods()) { ContextOperationBaseAttribute[] methodattr = method.GetCustomAttributes(typeof(ContextOperationBaseAttribute), false) as ContextOperationBaseAttribute[]; if (methodattr.Length <= 0) continue; ContextOperationBaseAttribute joinoper = _JoinOperation(methodattr); _contextRunTime.FilterMap.MapOperation(string.Format("{0}.{1}", method.DeclaringType.FullName, method.Name), joinoper); } } internal bool _IsEvening { get; set; } /// /// 后期动态绑定上下文。 /// internal void _EveningBoundChildClass () where ChildType : class { if (_contextRunTime != null) return;//说明之前已经进行过动态调用 _contextRunTime = ContextRuntime.CurrentContextRuntime;//动态绑定当前运行时上下文 _InitContextHandler (); } private ContextOperationBaseAttribute _JoinOperation(ContextOperationBaseAttribute[] operationarray) { //必须对数组进行排序后才能连接 for (int i = 0; i < operationarray.Length; i++) { for (int j = 0; j < i; j++) { if (operationarray[j].OperationSort > operationarray[j + 1].OperationSort) { ContextOperationBaseAttribute oper = operationarray[j]; operationarray[j] = operationarray[j + 1]; operationarray[j + 1] = oper; } } } ContextOperationBaseAttribute opernext = operationarray[0]; for (int i = 1; i < operationarray.Length; i++) { opernext.NextOperation = operationarray[i]; opernext = operationarray[i];//保持对当前循环对象的上级对象的引用。 } return operationarray[0]; } public MethodInfo GetMethodInfo(string methodname) { return this.GetType().GetMethod(methodname); } public override Guid InitPrimaryKey { get { return _contextRunTime.InitPrimaryKey; } } public override DateTime InitTime { get { return _contextRunTime.InitTime; } } public override object GetValue(object key) { return _contextRunTime.GetValue(key); } public override void SetValue(object key, object value) { _contextRunTime.SetValue(key, value); } } }
ContextModuleBaseObject 类主要实现的功能就是将对象动态的添加到当前上下文中。然后为AOP做些辅助性的工作,包括对类、属性、行为的特性元数据的缓存,这里只实现了行为的特性缓存。可以根据自己的需要扩展AOP的功能,在对象的属性上标记特性让属性也发挥作用。这里的特性就是AOP公布的指定接口。
2.3上下文对象的后期绑定实现
/*** * author:深度训练 * blog:http://wangqingpei557.blog.51cto.com/ * **/ using System; using System.Collections.Generic; using System.Text; namespace ContextModule { ////// 确定设置类是否需要后期动态绑定到上下文。 /// 使用该特性的类将是上下文活跃的,只有在使用的时候才确定当前上下文。 /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] public class ContextEveningBoundAttribute : Attribute { public ContextEveningBoundAttribute() { } private bool _isEvening; /// /// 指定对象是否需要后期动态绑定上下文。 /// public bool IsEvening { set { _isEvening = value; } get { return _isEvening; } } } }
public ContextModuleBaseObject() { if (typeof(T).GetCustomAttributes(typeof(ContextEveningBoundAttribute), false) != null) { _IsEvening = true; return; } //前期静态绑定上下文 if (ContextRuntime.CurrentContextRuntime == null) throw new Exception("上下文环境未能初始化,请检查您的代码入口是否启用了ContextRuntime对象。"); _contextRunTime = ContextRuntime.CurrentContextRuntime; _InitContextHandler(); }
到这里我们已经实现对象的动态绑定到上下文来,下面我们来分析Context如何用AOP配合完成面向切面编程的机制。
2.4.AOP中的对象行为的契约设计实现
其实这里的契约设计也就是图2中对AOP中的“面”的约定。
/*** * author:深度训练 * blog:http://wangqingpei557.blog.51cto.com/ * **/ using System; using System.Collections.Generic; using System.Text; namespace ContextModule { ////// 上下文操作动作特性化基类。 /// 所有对上下文中的类、方法的过滤操作都必须继承此类。 /// public abstract class ContextOperationBaseAttribute : Attribute, IContextOperationHandler { /// /// 过滤器的处理顺序,从小到大的方式进行处理。 /// public int OperationSort { get; set; } /// /// 下一次过滤操作类 /// internal ContextOperationBaseAttribute NextOperation { get; set; } /// /// 开始处理过滤对象 /// /// 方法的返回值类型 /// 调用的方法包装 /// 方法的有序参数 /// public virtual Result ResultAction (ContextMethodInfo actionmethod, params object[] paramarray) { object result = null; if (!actionmethod.IsPost) { result = (this as IContextOperationHandler).Operation(actionmethod, paramarray); if (this.NextOperation != null) return this.NextOperation.ResultAction (actionmethod, paramarray); } if (result != null) return (Result)result; return default(Result); } public abstract object Operation(ContextMethodInfo contextmethod, params object[] paramarray); } }
/*** * author:深度训练 * blog:http://wangqingpei557.blog.51cto.com/ * **/ using System; using System.Collections.Generic; using System.Text; namespace ContextModule { ////// 特定于上下文的过滤器映射表。 /// 上下文中的任何方法如果需要进行上下文管理的,则使用ContextModule.ContextOperationBaseAttribute特性派生类进行管理。 /// 所有附加于方法、类上的特性管理类都将被映射到ContextModule.ContextFilterHandlerMap实例中。 /// public class ContextFilterHandlerMap : Dictionary<string, ContextOperationBaseAttribute> { public ContextFilterHandlerMap() { } /// /// 获取方法对应的过滤器处理特性 /// /// 映射Key /// ContextOperationBaseAttribute特性实例 public ContextOperationBaseAttribute MapOperation(string mapname) { return this[mapname]; } /// /// 设置过滤器与特定方法的映射 /// /// 映射Key /// 过滤器特性基类ContextOperationBaseAttribute public void MapOperation(string mapname, ContextOperationBaseAttribute operationlist) { this.Add(mapname, operationlist); } } }
/*** * author:深度训练 * blog:http://wangqingpei557.blog.51cto.com/ * **/ using System; using System.Collections.Generic; using System.Text; using System.IO; namespace ContextModule { ////// 上下文操作管理接口 /// public interface IContextOperationHandler { /// /// 开始上下文处理 /// /// CRL目前正在执行的上下文方法的信息。 /// 可以通过ContextMethodInfo实例获取方法详细信息。 ///参数数组 object Operation(ContextMethodInfo contextmethod, params object[] paramarray); } }
通过对外公开接口,让实现“面”的客户端去完成对具体对象方法的执行。ContextMethodInfo类型是包装System.Reflection. MethodInfo 方法元数据的,将通过调用切入到方法内部。[王清培版权所有,转载请给出署名]
2.5.动态入口的实现
对所有方法的调用将是比较头疼的。由于一般面向上下文、面向切面都是有编写者控制对方法的调用,可以很方便的通过后台的隐式的调用。但是作为普通的方法的入口调用主要有三种方式实现。
1):委托实现入口
/*** * author:深度训练 * blog:http://wangqingpei557.blog.51cto.com/ * **/ using System; using System.Collections.Generic; using System.Text; using System.Reflection; namespace ContextModule { ////// 面向上下文的操作类。 /// 对上下文发起的方法调用需要通过该基类进行调用才能让我们的扩展点使用成为可能。 /// public static class ContextAction { /// /// 在面向上下文的环境中进行方法的调用。 /// /// 调用的上下文绑定对象类型 /// 方法的返回类型 /// 调用的上下文绑定对象的实例 /// 方法的信息对象MethodInfo,通过Oject.GetContextMethodInfo方法自动获取。 /// 方法的有序参数集合 /// ResultType泛型类型指定的返回实例 public static ResultType PostMethod (PostObjectType post, MethodInfo method, params object[] paramarray) where PostObjectType : ContextModuleBaseObject { _LockPostObejctIsEveningBound (post); string key = string.Format("{0}.{1}", method.DeclaringType.FullName, method.Name); if (!ContextRuntime.CurrentContextRuntime.FilterMap.ContainsKey(key)) { throw new Exception(string.Format("方法{0}未经过上下文进行管理。", key)); } ContextMethodInfo contextmethod = new ContextMethodInfo(method, post); return ContextRuntime.CurrentContextRuntime.FilterMap[key].ResultAction (contextmethod, paramarray); } /// /// 检查调用实例类是否属于后期绑定。 /// 通过使用ContextModule.ContextEveningBound(IsEvening = true)方式指定后期绑定上下文。 /// private static void _LockPostObejctIsEveningBound (PostObjectType post) where PostObjectType : ContextModuleBaseObject { ContextModuleBaseObject contextclass = post as ContextModuleBaseObject ; if (contextclass._IsEvening) contextclass._EveningBoundChildClass (); } } }
所有的调用均使用PostMethod泛型方法启动。_LockPostObejctIsEveningBound私有方法,判断当前类型是否是后期绑定,如果是则需要切入到基类中调用_ EveningBoundChildClass方法进行ContextOperationBaseAttribute 类型的链表构造,然后直接通过头对象进行调用。[王清培版权所有,转载请给出署名]
3.实例上下文与静态上下文
对于实例上下文同时也就存在静态上下文的概念,对于静态对象的逻辑归纳有点难度,由于静态对象在面向对象设计方面很难抽象。只能通过特性注入的方式强制性的将静态对象拉入上下文。但是在多线程的情况下,确实是可以研究的。将静态对象全部进行线程本地存储,强制性的进行类似实体对象的管理。
源码地址:http://files.cnblogs.com/wangiqngpei557/ContextModuleDemo.zip