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 { /// <summary> /// 上下文運行時環境。 /// 上下文邏輯運行時環境,環境中的功能都是可以通過附加進來的。 /// </summary> public class ContextRuntime : IDisposable { #region IDisposable成員 void IDisposable.Dispose() { _currentContextRuntime = null; } #endregion protected ContextRuntime() { } private DateTime _initTime = DateTime.Now; /// <summary> /// 獲取運行時創建上下文的時間 /// </summary> public virtual DateTime InitTime { get { return _initTime; } } private Dictionary<object, object> _runTimeResource = new Dictionary<object, object>(); private ContextFilterHandlerMap _filterMap = new ContextFilterHandlerMap(); /// <summary> /// 獲取上下文中的方法、類過濾器映射表 /// </summary> public ContextFilterHandlerMap FilterMap { get { return _filterMap; } } private Guid _initPrimaryKey = Guid.NewGuid(); /// <summary> /// 獲取運行時創建上下文的唯一標識 /// </summary> public virtual Guid InitPrimaryKey { get { return _initPrimaryKey; } } /// <summary> /// 獲取上下文共享區域中的數據 /// </summary> /// <param name="key">數據Key</param> /// <returns>object數據對象</returns> public virtual object GetValue(object key) { return _runTimeResource[key]; } /// <summary> /// 設置上下文共享區域中的數據 /// </summary> /// <param name="key">數據Key</param> /// <param name="value">要設置的數據對象</param> public virtual void SetValue(object key, object value) { _runTimeResource[key] = value; } [ThreadStatic] private static ContextRuntime _currentContextRuntime; /// <summary> /// 獲取當前上下文運行時對象. /// </summary> public static ContextRuntime CurrentContextRuntime { get { return _currentContextRuntime; } } /// <summary> /// 開始運行時上下文 /// </summary> /// <returns>ContextRuntime</returns> 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 { /// <summary> /// 上下綁定基類,強制派生類綁定到上下文。 /// 邏輯上下文的策略構造都在這里進行。 /// </summary> /// <typeparam name="T">受管理的上下文綁定對象類型,通常是ContextModuleBaseObject派生類。</typeparam> public class ContextModuleBaseObject<T> : ContextRuntime where T : class { /// <summary> /// 當前上下文綁定對象所綁定到的上下文物理對象實例。 /// </summary> 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<T>(); } /// <summary> /// 構造上下文的類過濾器、方法過濾器映射表。 /// </summary> private void _InitContextHandler<ChildType>() 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; } /// <summary> /// 后期動態綁定上下文。 /// </summary> internal void _EveningBoundChildClass<ChildType>() where ChildType : class { if (_contextRunTime != null) return;//說明之前已經進行過動態調用 _contextRunTime = ContextRuntime.CurrentContextRuntime;//動態綁定當前運行時上下文 _InitContextHandler<ChildType>(); } 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 { /// <summary> /// 確定設置類是否需要后期動態綁定到上下文。 /// 使用該特性的類將是上下文活躍的,只有在使用的時候才確定當前上下文。 /// </summary> [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] public class ContextEveningBoundAttribute : Attribute { public ContextEveningBoundAttribute() { } private bool _isEvening; /// <summary> /// 指定對象是否需要后期動態綁定上下文。 /// </summary> 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<T>(); }
到這里我們已經實現對象的動態綁定到上下文來,下面我們來分析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 { /// <summary> /// 上下文操作動作特性化基類。 /// 所有對上下文中的類、方法的過濾操作都必須繼承此類。 /// </summary> public abstract class ContextOperationBaseAttribute : Attribute, IContextOperationHandler { /// <summary> /// 過濾器的處理順序,從小到大的方式進行處理。 /// </summary> public int OperationSort { get; set; } /// <summary> /// 下一次過濾操作類 /// </summary> internal ContextOperationBaseAttribute NextOperation { get; set; } /// <summary> /// 開始處理過濾對象 /// </summary> /// <typeparam name="Result">方法的返回值類型</typeparam> /// <param name="actionmethod">調用的方法包裝</param> /// <param name="paramarray">方法的有序參數</param> /// <returns></returns> public virtual Result ResultAction<Result>(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<Result>(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 { /// <summary> /// 特定於上下文的過濾器映射表。 /// 上下文中的任何方法如果需要進行上下文管理的,則使用ContextModule.ContextOperationBaseAttribute特性派生類進行管理。 /// 所有附加於方法、類上的特性管理類都將被映射到ContextModule.ContextFilterHandlerMap實例中。 /// </summary> public class ContextFilterHandlerMap : Dictionary<string, ContextOperationBaseAttribute> { public ContextFilterHandlerMap() { } /// <summary> /// 獲取方法對應的過濾器處理特性 /// </summary> /// <param name="mapname">映射Key</param> /// <returns>ContextOperationBaseAttribute特性實例</returns> public ContextOperationBaseAttribute MapOperation(string mapname) { return this[mapname]; } /// <summary> /// 設置過濾器與特定方法的映射 /// </summary> /// <param name="mapname">映射Key</param> /// <param name="operationlist">過濾器特性基類ContextOperationBaseAttribute</param> 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 { /// <summary> /// 上下文操作管理接口 /// </summary> public interface IContextOperationHandler { /// <summary> /// 開始上下文處理 /// </summary> /// <param name="contextmethod">CRL目前正在執行的上下文方法的信息。 /// 可以通過ContextMethodInfo實例獲取方法詳細信息。</param> ///<param name="paramarray">參數數組</param> 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 { /// <summary> /// 面向上下文的操作類。 /// 對上下文發起的方法調用需要通過該基類進行調用才能讓我們的擴展點使用成為可能。 /// </summary> public static class ContextAction { /// <summary> /// 在面向上下文的環境中進行方法的調用。 /// </summary> /// <typeparam name="PostObjectType">調用的上下文綁定對象類型</typeparam> /// <typeparam name="ResultType">方法的返回類型</typeparam> /// <param name="post">調用的上下文綁定對象的實例</param> /// <param name="method">方法的信息對象MethodInfo,通過Oject.GetContextMethodInfo方法自動獲取。</param> /// <param name="paramarray">方法的有序參數集合</param> /// <returns>ResultType泛型類型指定的返回實例</returns> public static ResultType PostMethod<PostObjectType, ResultType>(PostObjectType post, MethodInfo method, params object[] paramarray) where PostObjectType : ContextModuleBaseObject<PostObjectType> { _LockPostObejctIsEveningBound<PostObjectType>(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<ResultType>(contextmethod, paramarray); } /// <summary> /// 檢查調用實例類是否屬於后期綁定。 /// 通過使用ContextModule.ContextEveningBound(IsEvening = true)方式指定后期綁定上下文。 /// </summary> private static void _LockPostObejctIsEveningBound<PostObjectType>(PostObjectType post) where PostObjectType : ContextModuleBaseObject<PostObjectType> { ContextModuleBaseObject<PostObjectType> contextclass = post as ContextModuleBaseObject<PostObjectType>; if (contextclass._IsEvening) contextclass._EveningBoundChildClass<PostObjectType>(); } } }
所有的調用均使用PostMethod泛型方法啟動。_LockPostObejctIsEveningBound私有方法,判斷當前類型是否是后期綁定,如果是則需要切入到基類中調用_ EveningBoundChildClass方法進行ContextOperationBaseAttribute 類型的鏈表構造,然后直接通過頭對象進行調用。[王清培版權所有,轉載請給出署名]
3.實例上下文與靜態上下文
對於實例上下文同時也就存在靜態上下文的概念,對於靜態對象的邏輯歸納有點難度,由於靜態對象在面向對象設計方面很難抽象。只能通過特性注入的方式強制性的將靜態對象拉入上下文。但是在多線程的情況下,確實是可以研究的。將靜態對象全部進行線程本地存儲,強制性的進行類似實體對象的管理。
源碼地址:http://files.cnblogs.com/wangiqngpei557/ContextModuleDemo.zip