自己实现简单的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 ...
随机推荐
- 删除Mysql数据表中多余的重复记录的sql语句
数据表 sniper_tb 中存在主键 id,字段url,现需要在url字段上添加 unique,但由于url存在重复记录,导致添加失败. 如何删除表中多余的url重复记录,仅保持一条? 思路一 将 ...
- TODO:小程序手机预览调试
TODO:小程序手机预览调试 1. 小程序注册,目前还未开通个人注册,主体类型为企业.政府.媒体.其他组织 2. 登录小程序,绑定开发者,获取AppID 3. 下载微信小程序示例-新片预告 https ...
- 我也来说说DDD~大话目录
回到占占推荐博客索引 DDD之前没有接触过,但一但有了接触就一发不可收拾,他会带去进入一个全新的世界! DDD不是新技术,而是新思想,新模式,是软件开发领域的一次突破,它更接近于业务,对于业务的改动它 ...
- react Props 验证 propTypes,
<body><!-- React 真实 DOM 将会插入到这里 --><div id="example"></div> <!- ...
- 关于angularjs中的jQuery
关于angularjs中的jQuery 下面是一个小例子,用来说明我经常看到的一种模式.我们需要一个开关型的按钮.(注意:这个例子的代码有点装逼,并且有点冗长,只是为了用来代表更加复杂一些的例子,这些 ...
- Security1:Create Login
Login 用于登陆SQL Server,语法是 -- Syntax for SQL Server CREATE LOGIN login_name { WITH <option_list1> ...
- 【Win 10应用开发】如何知道UAP在哪个平台上运行
面向22世纪的现代化应用程序可以同时在多种设备上运行,于是有朋友会有一个疑问:有时候,我们还真的需要判断一下,UAP应用程序在哪个平台上运行.尽管大多情况下我们不必要这样做,但某些特殊情况还得考虑.比 ...
- Vue.js学习笔记(8)拖放
小颖在目前负责的项目中,负责给同事提供所需组件,在这期间,我们家大颖姐姐让我 写个拖拽组件,一开始我是用click实现,先将你要拖拽的dom点一下,然后再点你要放的位置,这个dom再通过小颖写的方法, ...
- nodejs 批量修改、删除
aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAB4AAAAQhCAIAAABDaAVHAAAgAElEQVR4nOydeXxU5aH359733vd9b/ ...
- 爆一个VS2015 Update1更新带来的编译BUG【已有解决方案】
一个编译的BUG,在原生的VS2015中没有问题,但更新至VS2015 Update1之后就有了,大概是Update1用了新的编译器,害得我好苦.经测试,VS2013 Update5中也存在这个问题, ...