首页 > 学院 > 开发设计 > 正文

自己实现简单的AOP(三)实现增强四项基本功能

2019-11-14 15:48:59
字体:
来源:转载
供稿:网友

前面的两篇随笔,都是只是个铺垫,真正实现增强四项基本功能的重头戏,在本篇随笔中,

本文将通过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;        }        #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. 

 

未完待续...

下篇随笔,将实现简单的属性注入 及 被代理对象延迟初始化。

 


上一篇:WinDbg调试.NET程序入门

下一篇:几个月

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表