自己实现简单的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;}#endregionpublic 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.
未完待续...
下篇随笔,将实现简单的属性注入 及 被代理对象延迟初始化。
自己实现简单的AOP(三) 实现增强四项基本功能相关推荐
- 从头认识Spring-3.4 简单的AOP日志实现-扩展添加检查订单功能,以便记录并检測输入的參数...
这一章节我们再上一个章节的基础上加上一个检查订单功能 1.domain 蛋糕类: package com.raylee.my_new_spring.my_new_spring.ch03.topic_1 ...
- 简单理解TCP三次握手四次挥手(看一遍你就懂)
什么是TCP协议? TCP( Transmission control protocol )即传输控制协议,是一种面向连接.可靠的数据传输协议,它是为了在不可靠的互联网上提供可靠的端到端字节流而专门设 ...
- java after方法_spring AOP的After增强实现方法实例分析
本文实例讲述了spring AOP的After增强实现方法.分享给大家供大家参考,具体如下: 一 配置 xmlns:xsi="http://www.w3.org/2001/XMLSchema ...
- Flash Builder4.7极其简单破解方法-三步搞定(亲测)
资讯类型: 转载 来源页面: http://weibo.com/2101024913/yvmR0D9Df 资讯原标题: 资讯原作者: 丿卓越丶星辰 翻译词数: 词 我的评论: 对这篇文你有啥看法,跟贴 ...
- 分布式锁简单入门以及三种实现方式介绍(滴滴)
很多小伙伴在学习Java的时候,总是感觉Java多线程在实际的业务中很少使用,以至于不会花太多的时间去学习,技术债不断累积!等到了一定程度的时候对于与Java多线程相关的东西就很难理解,今天需要探讨的 ...
- Group Box组合框的简单使用 [大三TJB_708]
http://blog.csdn.net/misskissc/article/details/9317783 Group Box组合框的简单使用 [大三TJB_708] 转载于:https://www ...
- 手动实现一个迷你版的AOP(实战增强版)
在正式进行aop模块的介绍之前,我们需要先弄懂一些基本的术语概念. 在软件业,AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程,通过预编译方式和运行期间动 ...
- 【OGG】OGG简单配置双向复制(三)
[OGG]OGG简单配置双向复制(三) 一.1 BLOG文档结构图 一.2 前言部分 一.2.1 导读 各位技术爱好者,看完本文后,你可以掌握如下的技能,也可以学到一些其它你所不知道的知识,~O(∩_ ...
- Flash Builder4.7极其简单破解方法-三步搞定
Flash Builder4.7极其简单破解方法-三步搞定(亲测) 原方法适用于4.6版本,同样方法4.7完美破解,不敢独享 具体步骤如下: 1.到Adobe官网下载FlashBuilder 4.6, ...
最新文章
- 皮一皮:碰到这样的领导怎么办...
- 035_Unicode对照表一
- SVN服务器与客户端的配置
- LDAP 查询基本知识
- css 让div 的高度和屏幕的高度一样
- Html5开发之链接标签nav的用法
- python小明爬楼梯_爬楼梯(Python and C++解法)
- 解决在eclipse里没有“Dynamic Web Project“这个选项的问题
- 第九周-每周例行报告
- 《剑指offer》66道算法题合集(java实现)
- ESP32 学习笔记(二十一)电源管理
- vnr光学识别怎么打开_物流仓库安防监控系统安装的作用和功能
- 基于FPGA的AD9854并行接口驱动(VerilogHDL语言)
- 个人怎么做微信小程序?
- 服务器系统壁纸,云服务器壁纸
- 12步解N-S方程之第二步
- html输入浮点型,对于input框限定输入值为浮点型的js代码
- 习题9-4 查找书籍 (20分)PTA给定n本书的名称和定价,本题要求编写程序,查找并输出其中定价最高和最低的书的名称和定价。
- 面试心得与总结:BAT、网易、蘑菇街
- 年会直播方案应该怎么做
热门文章
- nslang oracle_解决ojdbc14连接oracle报“java.sql.SQLException: Io 异常: Size Data Unit (SDU) mismatch”异常问题...
- python统计学书籍推荐_一位90后统计学硕士的深悟:统计其实有门道!AI还能这样学!(精荐40本书+20视频资源...
- 故障码123401_电力系统规划设计对电力工程设计的应用
- monkey测试_安卓测试之monkey
- 学计算机需要带笔记本电脑,一年级学生必须带电脑上学吗?顾问给出建议,父母需要事先了解...
- phpstorm安装_快速打造自己的PHPStorm主题
- python如何测试仪器_使用python检测一个设备是否ping的通
- php 剪贴板,之Windows中的剪贴板
- Java追加写json_java – ObjectMapper追加文件JSON
- createbitmap 旋转90度_小学数学,图形的运动,平移与旋转