前面的兩篇隨筆,都是只是個鋪墊,真正實現增強四項基本功能的重頭戲,在本篇隨筆中,
本文將通過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(); }
為了統一處理錯誤,引入一自定義異常,所有需要拋出錯誤的地方,都拋出該異常即可。
/// <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; } #endregion public 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.VEVb.com/files/08shiyan/AOPDemo.zip
該源碼中,沒有針對當前隨筆,做相應的 demo.
未完待續...
下篇隨筆,將實現簡單的屬性注入 及 被代理對象延遲初始化。
新聞熱點
疑難解答