前面的兩篇隨筆,都是只是個(gè)鋪墊,真正實(shí)現(xiàn)增強(qiáng)四項(xiàng)基本功能的重頭戲,在本篇隨筆中,
本文將通過(guò)AOP實(shí)現(xiàn)如下的四個(gè)基本功能:
/// <para>1、自動(dòng)管理數(shù)據(jù)庫(kù)連接[可選]</para>
/// <para>2、自動(dòng)管理數(shù)據(jù)庫(kù)事務(wù),當(dāng)接收到異常后(無(wú)論什么異常)事務(wù)將自動(dòng)回滾[可選]</para>
/// <para>3、服務(wù)級(jí)加鎖[必選]</para>
/// <para>4、以統(tǒng)一方式處理 服務(wù)異常 及 錯(cuò)誤, 包括數(shù)據(jù)庫(kù)異常 和 主動(dòng)拋出的異常[必選]</para>
為了在完成3、4兩項(xiàng),需要在Service層基類中,引入幾個(gè)屬性和方法,以便協(xié)作完成相應(yīng)功能。
該Service基類,很簡(jiǎn)單,不用太多的解釋:
/// <summary> /// 擴(kuò)展的抽象服務(wù)類 /// <para>配合增強(qiáng)類,完成以下功能:</para> /// <para>1、自動(dòng)管理數(shù)據(jù)庫(kù)連接[可選]</para> /// <para>2、自動(dòng)管理數(shù)據(jù)庫(kù)事務(wù),當(dāng)接收到異常后(無(wú)論什么異常)事務(wù)將自動(dòng)回滾[可選]</para> /// /// <para>3、服務(wù)級(jí)加鎖[必選]</para> /// <para>4、以統(tǒng)一方式處理服務(wù)異常及錯(cuò)誤處理,包括數(shù)據(jù)庫(kù)異常 和 主動(dòng)拋出的異常[必選]</para> /// </summary> public abstract class ServiceAbstract : MarshalByRefObject { /// <summary> /// 是否發(fā)生錯(cuò)誤 /// </summary> public bool Error { get; PRotected set; } /// <summary> /// 錯(cuò)誤提示信息(友好的,用戶可見(jiàn)) /// </summary> public string ErrorMsg { get; protected set; } /// <summary> /// 錯(cuò)誤詳情 /// <para>所有錯(cuò)誤,均通過(guò)異常拋出</para> /// </summary> public Exception ErrorEx { get; protected set; } /// <summary> /// 重置錯(cuò)誤信息 /// </summary> public void ResetError() { this.Error = false; this.ErrorMsg = string.Empty; this.ErrorEx = null; } /// <summary> /// 設(shè)置錯(cuò)誤信息 /// </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> /// 獲取服務(wù)級(jí)別的鎖定對(duì)象,以完成系統(tǒng)應(yīng)用層加鎖(具體而言是Service層加鎖) /// </summary> /// <returns></returns> public abstract object GetLockObject(); }
為了統(tǒng)一處理錯(cuò)誤,引入一自定義異常,所有需要拋出錯(cuò)誤的地方,都拋出該異常即可。
/// <summary> /// 自定義的服務(wù)異常 /// </summary> [Serializable] public class ServiceException : Exception { /// <summary> /// 為異常提供附加數(shù)據(jù) /// <para>用戶不可見(jiàn)</para> /// </summary> public int Code { get; set; } /// <summary> /// 為異常提供附加數(shù)據(jù) /// <para>用戶不可見(jiàn)</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) { } }
重頭戲:抽象的增強(qiáng)類:
/// <summary> /// 抽象的服務(wù)增強(qiáng)類 /// <para>增強(qiáng)以下功能:</para> /// <para>1、自動(dòng)管理數(shù)據(jù)庫(kù)連接[可選]</para> /// <para>2、自動(dòng)管理數(shù)據(jù)庫(kù)事務(wù),當(dāng)接收到異常后(無(wú)論什么異常)事務(wù)將自動(dòng)回滾[可選]</para> /// /// <para>3、服務(wù)級(jí)加鎖[必選]</para> /// <para>4、以統(tǒng)一方式處理 服務(wù)異常 及 錯(cuò)誤, 包括數(shù)據(jù)庫(kù)異常 和 主動(dòng)拋出的異常[必選]</para> /// </summary> public abstract class ServiceAdviceAbstract<T> : AdviceAbstract where T : Exception { private static object objLock = new object(); #region 屬性 /// <summary> /// 是否保持(長(zhǎng))連接,即自動(dòng)管理連接 /// </summary> public bool KeepConnection { get; private set; } /// <summary> /// 是否使用事務(wù),即自動(dòng)管理事務(wù) /// </summary> public bool UseTransaction { get; private set; } /// <summary> /// 是否在當(dāng)前方法的增強(qiáng)中打開(kāi)了連接 /// </summary> protected bool CurrentKeepConnection { get; set; } /// <summary> /// 是否在當(dāng)前方法的增強(qiáng)中開(kāi)啟了事務(wù) /// </summary> protected bool CurrentUseTransaction { get; set; } #endregion #region 構(gòu)造函數(shù) /// <summary> /// /// </summary> /// <param name="keepConnection">是否保持(長(zhǎng))連接,即自動(dòng)管理連接</param> /// <param name="useTransaction">是否使用事務(wù),即自動(dòng)管理事務(wù)</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; // 服務(wù)類型校驗(yàn) 其拋出的異常不會(huì)被捕獲 Check(service); return LockInvoke(service, callMessage); } #region 可以擴(kuò)展的虛函數(shù) /// <summary> /// 執(zhí)行Lock加鎖調(diào)用 /// </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> /// 執(zhí)行Try...Catch增強(qiáng)調(diào)用 /// </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; } // 調(diào)用方法時(shí),內(nèi)部拋出的異常 catch (TargetInvocationException targetEx) { string msg = string.Empty; if (!(targetEx.InnerException is ServiceException)) { if (targetEx.InnerException is DbException) { msg = "數(shù)據(jù)異常:"; } else if (targetEx.InnerException is T) { msg = "服務(wù)異常:"; } else { msg = "系統(tǒng)異常:"; } } 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("數(shù)據(jù)異常:" + dbEx.Message, dbEx, target, callMessage); } catch (T tEx) { return ReturnError("服務(wù)異常:" + tEx.Message, tEx, target, callMessage); } catch (Exception ex) { return ReturnError("系統(tǒng)異常:" + ex.Message, ex, target, callMessage); } } /// <summary> /// 調(diào)用被代理對(duì)象方法前執(zhí)行 /// </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; } // 已經(jīng)開(kāi)啟了事務(wù) if (this.HasBeginTransaction()) { // 不需要在當(dāng)前方法的增強(qiáng)中進(jìn)行任何處理 return; } // 已經(jīng)打開(kāi)了連接 if (this.HasOpenConnection()) { if (this.UseTransaction) { this.BeginTransaction(true); this.CurrentUseTransaction = true; return; } return; } // 即沒(méi)有開(kāi)啟事務(wù),又沒(méi)有打開(kāi)連接 if (this.UseTransaction) { this.BeginTransaction(false); this.CurrentKeepConnection = true; this.CurrentUseTransaction = true; } else if (this.KeepConnection) { this.OpenConnection(); this.CurrentKeepConnection = true; } } /// <summary> /// 調(diào)用被代理對(duì)象方法后執(zhí)行 /// </summary> /// <param name="target"></param> protected virtual void AfterInvokeBeProxy(ServiceAbstract target) { // 當(dāng)前增強(qiáng) 只打開(kāi)了連接 if (this.CurrentKeepConnection && !this.CurrentUseTransaction) { this.CloseConnection(); } // 當(dāng)前增強(qiáng) 只開(kāi)啟了事務(wù) else if (!this.CurrentKeepConnection && this.CurrentUseTransaction) { this.CommitTransaction(true); } // 當(dāng)前增強(qiáng) 既打開(kāi)了連接,又開(kāi)啟了事務(wù) else if (this.CurrentKeepConnection && this.CurrentUseTransaction) { this.CommitTransaction(false); } } /// <summary> /// 返回錯(cuò)誤信息 /// <para>攔截所有異常,將錯(cuò)誤信息存儲(chǔ)到 ExtensionServiceAbstract 對(duì)象中,并返回被調(diào)用方法的默認(rèn)值</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 { // 當(dāng)前增強(qiáng) 只打開(kāi)了連接 if (this.CurrentKeepConnection && !this.CurrentUseTransaction) { this.CloseConnection(); } // 當(dāng)前增強(qiáng) 只開(kāi)啟了事務(wù) else if (!this.CurrentKeepConnection && this.CurrentUseTransaction) { this.RollBackTransaction(true); } // 當(dāng)前增強(qiáng) 既打開(kāi)了連接,又開(kāi)啟了事務(wù) else if (this.CurrentKeepConnection && this.CurrentUseTransaction) { this.RollBackTransaction(false); } } catch (Exception e) { Console.WriteLine(e.Message); } // 如果 邏輯上下文中已經(jīng)進(jìn)行了Try...Catch調(diào)用, // 則 將捕獲的異常向上層拋出 //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> /// 校驗(yàn)被代理的對(duì)象的類型 /// </summary> /// <param name="service"></param> protected virtual void Check(ServiceAbstract service) { if (service == null) { throw new ServiceException("服務(wù)增強(qiáng)類 AdviceAbstractGeneric 只能用于 MyBatisServiceAbstract類型的子類型 "); } } #endregion #region 管理數(shù)據(jù)庫(kù)連接和事務(wù) /// <summary> /// 打開(kāi)連接 /// </summary> protected abstract void OpenConnection(); /// <summary> /// 關(guān)閉連接 /// </summary> protected abstract void CloseConnection(); /// <summary> /// 開(kāi)啟事務(wù) /// </summary> protected abstract void BeginTransaction(bool onlyBeginTransaction); /// <summary> /// 提交事務(wù) /// </summary> protected abstract void CommitTransaction(bool onlyCommitTransaction); /// <summary> /// 回滾事務(wù) /// </summary> protected abstract void RollBackTransaction(bool onlyRollBackTransaction); /// <summary> /// 是否打開(kāi)了連接 /// </summary> /// <returns></returns> protected abstract bool HasOpenConnection(); /// <summary> /// 是否開(kāi)啟了事務(wù) /// </summary> /// <returns></returns> protected abstract bool HasBeginTransaction(); #endregion }
雖然,該類是抽象類,但四項(xiàng)基本功能,都已經(jīng)完成了。
另外,需要指出一點(diǎn)潛在bug:
當(dāng)Service方法嵌套調(diào)用Service方法的時(shí)候,如果內(nèi)層Service方法,拋出了異常,
會(huì)被內(nèi)層方法的增強(qiáng)所捕獲,而外層Service方法接收不到錯(cuò)誤信息。
正因如此,可能外層方法的事務(wù)無(wú)法像預(yù)料的那樣進(jìn)行回滾。
當(dāng)然,解決該問(wèn)題,相對(duì)而言也算簡(jiǎn)單,下篇隨筆再做說(shuō)明。
還有一點(diǎn)需要說(shuō)明:
我當(dāng)前的增強(qiáng)是基于iBatisNet的,其數(shù)據(jù)庫(kù)操作都是基于單例模式實(shí)現(xiàn)的(借助了HttpContext),
所以我將數(shù)據(jù)庫(kù)連接及事務(wù)管理的相關(guān)方法,放在了‘增強(qiáng)繼承體系’中,
如果使用其他方式處理數(shù)據(jù)庫(kù)連接或事務(wù),比較麻煩、可以考慮將相關(guān)方法,遷移到‘Service基類中’,放入‘Service繼承體系’。
借用Service層,連接Dao層,實(shí)現(xiàn)真正的數(shù)據(jù)庫(kù)操作(包括 連接 和 事務(wù))。
具體的實(shí)現(xiàn)方式,就留給大家去探究了。
附源碼(MVC4的項(xiàng)目 沒(méi)有packages文件夾):http://files.VEVb.com/files/08shiyan/AOPDemo.zip
該源碼中,沒(méi)有針對(duì)當(dāng)前隨筆,做相應(yīng)的 demo.
未完待續(xù)...
下篇隨筆,將實(shí)現(xiàn)簡(jiǎn)單的屬性注入 及 被代理對(duì)象延遲初始化。
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注