自己实现简单的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.
未完待续...
下篇随笔,将实现简单的属性注入 及 被代理对象延迟初始化。
自己实现简单的AOP(三) 实现增强四项基本功能的更多相关文章
- 从头认识Spring-3.4 简单的AOP日志实现-扩展添加检查订单功能,以便记录并检測输入的參数
这一章节我们再上一个章节的基础上加上一个检查订单功能 1.domain 蛋糕类: package com.raylee.my_new_spring.my_new_spring.ch03.topic_1 ...
- 自己实现简单的AOP(二)引入Attribute 为方法指定增强对象
话续前文 : 自己实现简单的AOP(一)简介 在前一篇文章中,对AOP的实现方式做了一个简单介绍.接下来,引入Attribute 为方法指定增强对象,由此实现一个简单的AOP. 注意:指定的是增强对象 ...
- 自己实现简单的AOP(一)简介
AOP 和 OOP,在我看来是两种相辅相成的技术,作为OOP的补充,AOP 有着自己特殊的应用场景. 假设,我们需要在Service层实现以下几项基本功能: /// <para>1.自动管 ...
- 自己实现简单的AOP(四)自动初始化代理对象
前面三篇随笔,已经完成了AOP的核心功能,但 代理对象的初始化还是有些麻烦,本文将解决该问题. Demo 片段如下: public class HomeController : Controller ...
- Spring学习之Aop的各种增强方法
AspectJ允许使用注解用于定义切面.切入点和增强处理,而Spring框架则可以识别并根据这些注解来生成AOP代理.Spring只是使用了和AspectJ 5一样的注解,但并没有使用AspectJ的 ...
- 从头认识Spring-3.1 简单的AOP日志实现-某方法之前的前后记录日志
这一章节我们引入简单的AOP日志实现. 1.domain 蛋糕类: package com.raylee.my_new_spring.my_new_spring.ch03.topic_1_1; pub ...
- iOS开发UI篇—Quartz2D简单使用(三)
iOS开发UI篇—Quartz2D简单使用(三) 一.通过slider控制圆的缩放 1.实现过程 新建一个项目,新建一个继承自UIview的类,并和storyboard中自定义的view进行关联. 界 ...
- XML系列之--对电文格式XML的简单操作(三)
前两章介绍了关于Linq创建.解析SOAP格式的XML,在实际运用中,可能会对xml进行一些其它的操作,比如基础的增删该查,而操作对象首先需要获取对象,针对于DOM操作来说,Linq确实方便了不少,如 ...
- python练习题-简单方法判断三个数能否组成三角形
python简单方法判断三个数能否组成三角形 #encoding=utf-8 import math while True: str=raw_input("please input thre ...
随机推荐
- TCPView for Windows
TCPView是一个用来显示系统中所有的TCP和UDP端点(endpoint)列表的Windows程序,包括本地和远程的网络地址,以及TCP连接的状态.在Windows Server 2008.Vis ...
- 操作系统核心原理-7.设备管理:I/O原理
一.I/O的基本知识 1.1 为何要有I/O 前面阐述了操作系统具有进程管理.内存管理.外存管理三大核心功能,但是计算机归根是为人类服务的,这就要求计算机必须提供某种机制使得人们可以向计算机发出命令或 ...
- Unicode编码解码在线转换工具
// Unicode编码解码在线转换工具 Unicode 是基于通用字符集(Universal Character Set)的标准来发展,并且同时也以书本的形式(The Unicode Standar ...
- “安装项目” Step By Step
目录 一 基本操作 二 定制安装对话框 三 安装程序类 四 总结 .NET程序集因为包含了元数据,所以程序集具有自描述性.多数程序自身包含了运行所需要的全部信息,这类程序集就是我们常说的“绿色软件”. ...
- gulp使用小结(二)
接上篇文章接Gulp使用小结(一) 内容如下: 首先,偶在gulp-demos上已经提交了个较通用的栗子...俺琢磨半天,原准备分阶段搞些 Gulp 套路,但是写完介个栗子之后,觉得已经能覆盖绝大多数 ...
- JS实战 · 实践积累点滴杂烩
onmouseover : 鼠标进入 onmouseout : 鼠标离开 onfocus:得到焦点 表单提交执行JS代码,有两种常用方式. 一:在局部(比如按钮定义处)用onclick=" ...
- yar框架使用笔记
Yar是什么 Yar是并行的RPC框架(Concurrent RPC framework),Laruence开发. 安装 下载地址:http://pecl.php.net/package/yar wi ...
- 2013 duilib入门简明教程 -- FAQ (19)
虽然前面的教程几乎把所有的知识点都罗列了,但是有很多问题经常在群里出现,所以这里再次整理一下. 需要注意的是,在下面的问题中,除了加上XML属性外,主窗口必须继承自WindowImpl ...
- Angularjs1培训
Angularjs1培训: angularjs解决什么问题? 从无穷无尽的DOM操作中解放出来,专注于业务逻辑,DOM操作不叫业务逻辑,那是试图呈现. 组件化,模块化为构建大型项目铺平道路,模块发开发 ...
- Screeps ———— A MMO Strategy Sandbox Game for Programmers
At the beginning, let's see three of this game's captures. Yes, As what you see in these pictures, y ...