自己实现简单的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 ...
随机推荐
- Android内存清理
直接上图吧! 获取文件大小 ,清理文件工具类 public class DataCleanManager { public static String getTotalCacheSize(Contex ...
- 面试题系列——OSI七层模型
OSI(开放式系统互联模型)是一个开放性的通行系统互连参考模型,是一个协议规范.它把网络协议从逻辑上分了七层,每一层都有对应的物理设备. OSI七层模型是一种框架性的设计方法,设计的主要目的是为了解决 ...
- 解读2015年互联网UGC内容发展态势,安全事件频发
<2015内容安全年报> 阿里移动安全 第一章 2015年内容安全形势 随着互联网业务的迅速发展,互联网上的信息内容带来了爆炸式的增长.由于缺乏对网络活动进行有效监督和管理的措施,致使互联 ...
- Logging with Log4net (二)
log4net 是.net 的一款日志记录框架. 它提供了很多的方法来帮助记录日志: 使用起来也比较方便: 选中项目,点击右键,然后选择 Manage NuGet Packages... 安装log4 ...
- 使用 nvm 管理不同版本的 node 与 npm
补充说明:Mac 下通过 brew install nvm 所安装的 nvm ,由于安装路径不同,无法正确启用.建议使用 brew uninstall nvm 卸载掉之后,通过本文的方案重新安装一次. ...
- 剑指Offer面试题:22.二叉搜索树的后序遍历序列
一.题目:二叉搜索树的后序遍历序列 题目:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果.如果是则返回true,否则返回false.假设输入的数组的任意两个数字都互不相同. 例如在下面 ...
- Web前端开发工程师养成计划【转载】
Web前端开发工程师养成计划(入门篇) 最原始的忠告:这个世界上有想法的人很多,但是有想法又能实现它的人太少! 首先要感谢伟大的Web2.0概念.产品概念.用户体验概念.jQuery插件,是它们在中国 ...
- 玩转Windows服务系列——命令行管理Windows服务
说到Windows服务的管理就不得不说通过命令行的方式管理Windows服务,因为无论是系统管理员,还是通过编程的方式调用cmd命令,命令行都是非常方便以及强大的工具. 接下来就看一下如何通过cmd命 ...
- 可以这样去理解group by和聚合函数
写在前面的话:用了好久group by,今天早上一觉醒来,突然感觉group by好陌生,总有个筋别不过来,为什么不能够select * from Table group by id,为什么一定不能是 ...
- ORM小练习代码
DOG类 namespace RupengORM { public class Dog { public Dog() { } /// <summary> /// 显示提供无参构造函数 // ...