自己实现简单的AOP(三) 实现增强四项基本功能
前面的兩篇隨筆,都是只是個鋪墊,真正實現增強四項基本功能的重頭戲,在本篇隨筆中,
本文將通過AOP實現如下的四個基本功能:
/// <para>1、自動管理數據庫連接[可選]</para>
/// <para>2、自動管理數據庫事務,當接收到異常后(無論什么異常)事務將自動回滾[可選]</para>
/// <para>3、服務級加鎖[必選]</para>
/// <para>4、以統一方式處理 服務異常 及 錯誤, 包括數據庫異常 和 主動拋出的異常[必選]</para>
?
為了在完成3、4兩項,需要在Service層基類中,引入幾個屬性和方法,以便協作完成相應功能。
該Service基類,很簡單,不用太多的解釋:
/// <summary>/// 擴展的抽象服務類/// <para>配合增強類,完成以下功能:</para>/// <para>1、自動管理數據庫連接[可選]</para>/// <para>2、自動管理數據庫事務,當接收到異常后(無論什么異常)事務將自動回滾[可選]</para>/// /// <para>3、服務級加鎖[必選]</para>/// <para>4、以統一方式處理服務異常及錯誤處理,包括數據庫異常 和 主動拋出的異常[必選]</para>/// </summary>public abstract class ServiceAbstract : MarshalByRefObject{/// <summary>/// 是否發生錯誤/// </summary>public bool Error { get; protected set; }/// <summary>/// 錯誤提示信息(友好的,用戶可見)/// </summary>public string ErrorMsg { get; protected set; }/// <summary>/// 錯誤詳情/// <para>所有錯誤,均通過異常拋出</para>/// </summary>public Exception ErrorEx { get; protected set; }/// <summary>/// 重置錯誤信息/// </summary>public void ResetError(){this.Error = false;this.ErrorMsg = string.Empty;this.ErrorEx = null;}/// <summary>/// 設置錯誤信息/// </summary>/// <param name="msg"></param>/// <param name="ex"></param>public void SetError(string msg, Exception ex){this.Error = true;this.ErrorEx = ex;this.ErrorMsg = msg;}/// <summary>/// 獲取服務級別的鎖定對象,以完成系統應用層加鎖(具體而言是Service層加鎖)/// </summary>/// <returns></returns>public abstract object GetLockObject();} Service基類?
為了統一處理錯誤,引入一自定義異常,所有需要拋出錯誤的地方,都拋出該異常即可。
/// <summary>/// 自定義的服務異常/// </summary> [Serializable]public class ServiceException : Exception{/// <summary>/// 為異常提供附加數據/// <para>用戶不可見</para>/// </summary>public int Code { get; set; }/// <summary>/// 為異常提供附加數據/// <para>用戶不可見</para>/// </summary>public string Tag { get; set; }public ServiceException() { }public ServiceException(string message) : base(message) { }public ServiceException(string message, Exception inner) : base(message, inner) { }protected ServiceException(System.Runtime.Serialization.SerializationInfo info,System.Runtime.Serialization.StreamingContext context): base(info, context) { }} 自定義異常?
重頭戲:抽象的增強類:
/// <summary>/// 抽象的服務增強類/// <para>增強以下功能:</para>/// <para>1、自動管理數據庫連接[可選]</para>/// <para>2、自動管理數據庫事務,當接收到異常后(無論什么異常)事務將自動回滾[可選]</para>/// /// <para>3、服務級加鎖[必選]</para>/// <para>4、以統一方式處理 服務異常 及 錯誤, 包括數據庫異常 和 主動拋出的異常[必選]</para>/// </summary>public abstract class ServiceAdviceAbstract<T> : AdviceAbstract where T : Exception{private static object objLock = new object();#region 屬性/// <summary>/// 是否保持(長)連接,即自動管理連接/// </summary>public bool KeepConnection { get; private set; }/// <summary>/// 是否使用事務,即自動管理事務/// </summary>public bool UseTransaction { get; private set; }/// <summary>/// 是否在當前方法的增強中打開了連接/// </summary>protected bool CurrentKeepConnection { get; set; }/// <summary>/// 是否在當前方法的增強中開啟了事務/// </summary>protected bool CurrentUseTransaction { get; set; }#endregion#region 構造函數/// <summary>/// /// </summary>/// <param name="keepConnection">是否保持(長)連接,即自動管理連接</param>/// <param name="useTransaction">是否使用事務,即自動管理事務</param>public ServiceAdviceAbstract(bool keepConnection, bool useTransaction){this.KeepConnection = keepConnection;this.UseTransaction = useTransaction;}#endregionpublic sealed override IMessage Invoke(MarshalByRefObject target, IMethodCallMessage callMessage){ServiceAbstract service = target as ServiceAbstract;// 服務類型校驗 其拋出的異常不會被捕獲 Check(service);return LockInvoke(service, callMessage);}#region 可以擴展的虛函數/// <summary>/// 執行Lock加鎖調用/// </summary>/// <param name="target"></param>/// <param name="callMessage"></param>/// <returns></returns>protected virtual IMessage LockInvoke(ServiceAbstract target, IMethodCallMessage callMessage){lock (target.GetLockObject()){return CatchAdviceInvoke(target, callMessage);}}/// <summary>/// 執行Try...Catch增強調用/// </summary>/// <param name="target"></param>/// <param name="callMessage"></param>/// <returns></returns>protected virtual IMessage CatchAdviceInvoke(ServiceAbstract target, IMethodCallMessage callMessage){try{BeforeInvokeBeProxy(target);IMessage message = DelayProxyUtil.InvokeBeProxy(target, callMessage);AfterInvokeBeProxy(target);return message;}// 調用方法時,內部拋出的異常catch (TargetInvocationException targetEx){string msg = string.Empty;if (!(targetEx.InnerException is ServiceException)){if (targetEx.InnerException is DbException){msg = "數據異常:";}else if (targetEx.InnerException is T){msg = "服務異常:";}else{msg = "系統異常:";}}return ReturnError(msg + targetEx.InnerException.Message, targetEx.InnerException, target, callMessage);}catch (ServiceException sEx){return ReturnError(sEx.Message, sEx, target, callMessage);}catch (DbException dbEx){return ReturnError("數據異常:" + dbEx.Message, dbEx, target, callMessage);}catch (T tEx){return ReturnError("服務異常:" + tEx.Message, tEx, target, callMessage);}catch (Exception ex){return ReturnError("系統異常:" + ex.Message, ex, target, callMessage);}}/// <summary>/// 調用被代理對象方法前執行/// </summary>/// <param name="target"></param>protected virtual void BeforeInvokeBeProxy(ServiceAbstract target){target.ResetError();this.CurrentKeepConnection = false;this.CurrentUseTransaction = false;if (!this.KeepConnection && !this.UseTransaction){return;}// 已經開啟了事務 if (this.HasBeginTransaction()){// 不需要在當前方法的增強中進行任何處理return;}// 已經打開了連接if (this.HasOpenConnection()){if (this.UseTransaction){this.BeginTransaction(true);this.CurrentUseTransaction = true;return;}return;}// 即沒有開啟事務,又沒有打開連接if (this.UseTransaction){this.BeginTransaction(false);this.CurrentKeepConnection = true;this.CurrentUseTransaction = true;}else if (this.KeepConnection){this.OpenConnection();this.CurrentKeepConnection = true;}}/// <summary>/// 調用被代理對象方法后執行/// </summary>/// <param name="target"></param>protected virtual void AfterInvokeBeProxy(ServiceAbstract target){// 當前增強 只打開了連接if (this.CurrentKeepConnection && !this.CurrentUseTransaction){this.CloseConnection();}// 當前增強 只開啟了事務else if (!this.CurrentKeepConnection && this.CurrentUseTransaction){this.CommitTransaction(true);}// 當前增強 既打開了連接,又開啟了事務else if (this.CurrentKeepConnection && this.CurrentUseTransaction){this.CommitTransaction(false);}}/// <summary>/// 返回錯誤信息/// <para>攔截所有異常,將錯誤信息存儲到 ExtensionServiceAbstract 對象中,并返回被調用方法的默認值</para>/// </summary>/// <param name="msg"></param>/// <param name="ex"></param>/// <param name="target"></param>/// <param name="callMessage"></param>/// <returns></returns>protected virtual IMessage ReturnError(string msg, Exception ex,ServiceAbstract target, IMethodCallMessage callMessage){try{// 當前增強 只打開了連接if (this.CurrentKeepConnection && !this.CurrentUseTransaction){this.CloseConnection();}// 當前增強 只開啟了事務else if (!this.CurrentKeepConnection && this.CurrentUseTransaction){this.RollBackTransaction(true);}// 當前增強 既打開了連接,又開啟了事務else if (this.CurrentKeepConnection && this.CurrentUseTransaction){this.RollBackTransaction(false);}}catch (Exception e){Console.WriteLine(e.Message);}// 如果 邏輯上下文中已經進行了Try...Catch調用,// 則 將捕獲的異常向上層拋出//if (this.HasTryCatch)//{// return DelayProxyUtil.ReturnExecption(ex, callMessage);//} target.SetError(msg, ex);// 記錄日志 WriteLog(ex);return DelayProxyUtil.ReturnDefaultValue(target, callMessage);}/// <summary>/// 記錄日志/// </summary>/// <param name="ex"></param>protected virtual void WriteLog(Exception ex){}/// <summary>/// 校驗被代理的對象的類型/// </summary>/// <param name="service"></param>protected virtual void Check(ServiceAbstract service){if (service == null){throw new ServiceException("服務增強類 AdviceAbstractGeneric 只能用于 MyBatisServiceAbstract類型的子類型 ");}}#endregion#region 管理數據庫連接和事務/// <summary>/// 打開連接/// </summary>protected abstract void OpenConnection();/// <summary>/// 關閉連接/// </summary>protected abstract void CloseConnection();/// <summary>/// 開啟事務/// </summary>protected abstract void BeginTransaction(bool onlyBeginTransaction);/// <summary>/// 提交事務/// </summary>protected abstract void CommitTransaction(bool onlyCommitTransaction);/// <summary>/// 回滾事務/// </summary>protected abstract void RollBackTransaction(bool onlyRollBackTransaction);/// <summary>/// 是否打開了連接/// </summary>/// <returns></returns>protected abstract bool HasOpenConnection();/// <summary>/// 是否開啟了事務/// </summary>/// <returns></returns>protected abstract bool HasBeginTransaction();#endregion} 抽象的增強類?
雖然,該類是抽象類,但四項基本功能,都已經完成了。
?
另外,需要指出一點潛在bug:
當Service方法嵌套調用Service方法的時候,如果內層Service方法,拋出了異常,
會被內層方法的增強所捕獲,而外層Service方法接收不到錯誤信息。
正因如此,可能外層方法的事務無法像預料的那樣進行回滾。
當然,解決該問題,相對而言也算簡單,下篇隨筆再做說明。
?
還有一點需要說明:
我當前的增強是基于iBatisNet的,其數據庫操作都是基于單例模式實現的(借助了HttpContext),
所以我將數據庫連接及事務管理的相關方法,放在了‘增強繼承體系’中,
如果使用其他方式處理數據庫連接或事務,比較麻煩、可以考慮將相關方法,遷移到‘Service基類中’,放入‘Service繼承體系’。
借用Service層,連接Dao層,實現真正的數據庫操作(包括 連接 和 事務)。
具體的實現方式,就留給大家去探究了。
?
附源碼(MVC4的項目 沒有packages文件夾):http://files.cnblogs.com/files/08shiyan/AOPDemo.zip?
該源碼中,沒有針對當前隨筆,做相應的 demo.?
?
未完待續...
下篇隨筆,將實現簡單的屬性注入 及 被代理對象延遲初始化。
?
總結
以上是生活随笔為你收集整理的自己实现简单的AOP(三) 实现增强四项基本功能的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C#语法精髓之常用的操作符
- 下一篇: ssh无密码登陆