自己实现简单的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(); }
为了统一处理错误,引入一自定义异常,所有需要抛出错误的地方,都抛出该异常即可。
/// <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文件夹):https://files.cnblogs.com/files/08shiyan/AOPDemo.zip
该源码中,没有针对当前随笔,做相应的 demo.
未完待续...
下篇随笔,将实现简单的属性注入 及 被代理对象延迟初始化。