前面的两篇随笔,都是只是个铺垫,真正实现增强四项基本功能的重头戏,在本篇随笔中,

本文将通过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(三) 实现增强四项基本功能的更多相关文章

  1. 从头认识Spring-3.4 简单的AOP日志实现-扩展添加检查订单功能,以便记录并检測输入的參数

    这一章节我们再上一个章节的基础上加上一个检查订单功能 1.domain 蛋糕类: package com.raylee.my_new_spring.my_new_spring.ch03.topic_1 ...

  2. 自己实现简单的AOP(二)引入Attribute 为方法指定增强对象

    话续前文 : 自己实现简单的AOP(一)简介 在前一篇文章中,对AOP的实现方式做了一个简单介绍.接下来,引入Attribute 为方法指定增强对象,由此实现一个简单的AOP. 注意:指定的是增强对象 ...

  3. 自己实现简单的AOP(一)简介

    AOP 和 OOP,在我看来是两种相辅相成的技术,作为OOP的补充,AOP 有着自己特殊的应用场景. 假设,我们需要在Service层实现以下几项基本功能: /// <para>1.自动管 ...

  4. 自己实现简单的AOP(四)自动初始化代理对象

    前面三篇随笔,已经完成了AOP的核心功能,但 代理对象的初始化还是有些麻烦,本文将解决该问题. Demo 片段如下: public class HomeController : Controller ...

  5. Spring学习之Aop的各种增强方法

    AspectJ允许使用注解用于定义切面.切入点和增强处理,而Spring框架则可以识别并根据这些注解来生成AOP代理.Spring只是使用了和AspectJ 5一样的注解,但并没有使用AspectJ的 ...

  6. 从头认识Spring-3.1 简单的AOP日志实现-某方法之前的前后记录日志

    这一章节我们引入简单的AOP日志实现. 1.domain 蛋糕类: package com.raylee.my_new_spring.my_new_spring.ch03.topic_1_1; pub ...

  7. iOS开发UI篇—Quartz2D简单使用(三)

    iOS开发UI篇—Quartz2D简单使用(三) 一.通过slider控制圆的缩放 1.实现过程 新建一个项目,新建一个继承自UIview的类,并和storyboard中自定义的view进行关联. 界 ...

  8. XML系列之--对电文格式XML的简单操作(三)

    前两章介绍了关于Linq创建.解析SOAP格式的XML,在实际运用中,可能会对xml进行一些其它的操作,比如基础的增删该查,而操作对象首先需要获取对象,针对于DOM操作来说,Linq确实方便了不少,如 ...

  9. python练习题-简单方法判断三个数能否组成三角形

    python简单方法判断三个数能否组成三角形 #encoding=utf-8 import math while True: str=raw_input("please input thre ...

随机推荐

  1. perl 删除过期文件

    #!/usr/bin/perl `find /bak/ >list.txt`; open LIST,"/root/list.txt"; while (<LIST> ...

  2. SQL Server2016 原生支持JSON

    SQL Server2016 原生支持JSON SQL Server 2005 开始支持 XML 数据类型,提供原生的 XML数据类型.XML 索引以及各种管理 XML 或输出 XML 格式的函数. ...

  3. IE7中使用Jquery动态操作name问题

    问题:IE7中无法使用Jquery动态操作页面元素的name属性. 在项目中有出现问题,某些客户的机器偶尔会有,后台取不到前台的数据值. 然开发和测试环境总是不能重现问题.坑爹之处就在于此,不能重现就 ...

  4. [译]MVC网站教程(三):动态布局和站点管理

    目录 1.   介绍 2.   软件环境 3.   在运行示例代码之前(源代码 + 示例登陆帐号) 4.   自定义操作结果和控制器扩展 1)   OpenFileResult 2)   ImageR ...

  5. Linux2 在Linux(CentOS)上配置SSH免登陆

    前言:      本文主要是我在安装hadoop之前,需要先配置SSH免登陆.通过网上搜索,发现不少类似的资料,但多少都有些小问题,所以结合自己的实践,记录在此,作为参考.如果能帮助到其他人,自然是更 ...

  6. Nova PhoneGap框架 第三章 页面

    页面在项目架构中是一个很重要的概念,它让我们能够将一个功能复杂的项目拆分成一个一个功能比较独立的小区域,这极大的提高了代码的可读性和可维护性. 在我们这个框架中,一个页面由JS和HTML两部分组成,首 ...

  7. .NET事件监听机制的局限与扩展

    .NET中把“事件”看作一个基本的编程概念,并提供了非常优美的语法支持,对比如下C#和Java代码可以看出两种语言设计思想之间的差异. // C#someButton.Click += OnSomeB ...

  8. Zookeeper API for JAVA实战与应用

    package com.zookeeper.watcher; import java.util.List; import java.util.concurrent.CountDownLatch; im ...

  9. Atitit 深入理解软件的本质 attilax总结 软件三原则"三次原则"是DRY原则和YAGNI原则的折

    Atitit 深入理解软件的本质 attilax总结 软件三原则"三次原则"是DRY原则和YAGNI原则的折 1.1.1. 软件的本质:抽象  1 1.2. 软件开发的过程就是不断 ...

  10. Text文档编码识别方法

    Text文档编码识别方法 在做文档读取的时候,时常碰到编码格式不正确的问题,而要怎么样正确识别文档的编码格式,成了很多程序员的一块心病,今天我就要试着治好这块心病,这段代码的浓缩来自上千万文档的数据分 ...