zoukankan      html  css  js  c++  java
  • 自己实现简单的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;
            }
    
            #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.cnblogs.com/files/08shiyan/AOPDemo.zip 

    该源码中,没有针对当前随笔,做相应的 demo. 

    未完待续...

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

  • 相关阅读:
    【杂谈】压行技巧(压代码)
    【UVA】11464 Even Parity(枚举子集)
    【POJ】2373 Dividing the Path(单调队列优化dp)
    【POJ】2329 Nearest number
    【BZOJ】1833: [ZJOI2010] count 数字计数(数位dp)
    【BZOJ】2809: [Apio2012]dispatching(左偏树)
    【BZOJ】2342: [Shoi2011]双倍回文(Manacher)
    【BZOJ】1912: [Apio2010]patrol 巡逻(树的直径)
    【BZOJ】1911: [Apio2010]特别行动队(斜率优化dp)
    【BZOJ】1913: [Apio2010]signaling 信号覆盖(计算几何+计数)
  • 原文地址:https://www.cnblogs.com/08shiyan/p/4811050.html
Copyright © 2011-2022 走看看