AOP(面向切面编程:Aspect Oriented Programming)为诸如日志记录、性能统计、安全控制、事务处理、异常处理等与具体业务逻辑无关,却需要在全局范围进行执行的功能提供了一种良好重用和与业务逻辑解耦的实现思路。AOP思想是围绕着切面进行的,所谓“切面”就是目标对象的某种操作,其基本过程是在系统其它部分调用目标对象的某种操作时拦截这些调用,在进行真正的调用前/后执行一段中间逻辑,并根据中间逻辑的执行结果决定是否进行真实调用或者修改返回结果。
AOP带来的好处是明显,但是我们怎么在项目中应用AOP呢?目前AOP在Java领域有一些较成熟的框架诸如 AspectJ、Spring AOP 等,在.NET领域有AspectC#、Castle等。尽管有现成的框架可以使用,但是由于存在学习曲线问题和框架的成熟度问题,使得很多项目通常都不会贸然使用第三方的框架。如果我们自己能够根据项目需要按需设计自己的轻量AOP组件/框架,这就能够给我们的项目带来良好的伸缩性。
包括现有的框架在内,AOP的实现方式通常被分为“静态织入”和“动态织入”两种。采用静态织入方式的框架是通过扩展编译器对代码的中间语言(IL)插入代码的方式实现对目标对象的调用拦截。动态织入方式在.NET中可以有两种实现:采用“装饰者模式”设计项目类库来实现;基于透明代理(TransparentProxy)/真实代理(RealProxy)来实现。下面就介绍动态织入的这两种实现。
- 最简单的AOP实现:采用“装饰者模式”实现方法调用拦截
上图是GoF装饰者模式的经典类图,此处的实现类似于上图,基本原理是在封装对象的创建过程,在创建类型的实例时并不返回类型的真正实例,而是返回一个包装了真实对象的”Decorator”。以下的代码展示了这一实现过程。
首先是定义一个公共的接口 IDataObject:
///
/// 数据对象接口;///
public interface IDataObject
{ ///
int Compute();
}
接下来定义充当”Decorator”的 DataObjectProxy :
DataObjectProxy
///
/// IDataObject 代理;实现对 IDataObject 操作的拦截;///
public class DataObjectProxy : IDataObject
{ private IDataObject _realObject; public DataObjectProxy(IDataObject realObject)
{
_realObject = realObject;
}///
public int Compute()
{
DoSomethingBeforeCompute();int result = _realObject.Compute();
DoSomethingAfterCompute(result);
return result;
}private void DoSomethingAfterCompute(int result)
{
Console.WriteLine(“After Compute “ + _realObject.ToString() + “. Result=” + result);
}private void DoSomethingBeforeCompute()
{
Console.WriteLine(“Before Compute “ + _realObject.ToString());
}
}
DataObjectProxy拦截了对真实对象的 Compute 方法的调用,在 Compute 前后输出控制台信息。
定义真正的实现 DataObject
DataObject
///
/// 数据对象;///
public class DataObject : IDataObject
{ private int _parameter1; private int _parameter2;///
private DataObject()
{
}///
public static IDataObject CreateDataObject(int p1, int p2)
{ //创建真实实例;
DataObject realObject = new DataObject();
realObject._parameter1 = p1;
realObject._parameter2 = p2;//返回代理;
return new DataObjectProxy(realObject);
}///
public int Compute()
{ return -1;
}
}
DataObject 通过将构造函数定义为 private 封装其实例化过程。要获得一个 IDataObject 的实例就必需通过静态方法 CreateDataObject 。而用DataObjectProxy代替DataObject的偷天换日的过程就是在 CreateDataObject 方法中完成的。
以下的代码将展示这一拦截过程
public static void Main(string[] args)
{
IDataObject dtObj = DataObject.CreateDataObject(10, 6); int result = dtObj.Compute();
Console.ReadLine();
}
代码在控制台中的输出如下:
上面的输出表明我们针对 Compute 方法的调用像期望的那样被拦截了。
但是我们看到这种AOP的实现方式有其局限性:首先得基于一个特定的接口进行定义,无法创建通用的 Proxy 对象;其次对每一个要拦截的方法都要进行编码实现,无法重用。这些局限使得这种实现方法只能在局部使用,如果在大范围的使用还是存在许多重复性的编码。以下介绍的基于透明代理(TransparentProxy)/真实代理(RealProxy)的实现将解决这些问题。
- 基于透明代理(TransparentProxy)/真实代理(RealProxy)实现方法调用拦截
真实代理和透明代理机制是由 .NET Remoting 提供的,此处所说的真实代理(RealProxy)特指 System.Runtime.Remoting.Proxies 命名空间中的 RealProxy 类型。关于透明代理和真实代理的机制及相关概念请参阅 MSDN 文档,在此就不再赘述,直接用代码来表达。
下面基于真实代理(RealProxy)定义了一个通用的代理对象 AopProxy
通用代理:AopProxy
///
/// 通用的代理; ///
///
class AopProxy<T> : RealProxy
{ private T _realObject;public AopProxy(T realObject)
:base(typeof(T))
{
_realObject = realObject;
}///
public override IMessage Invoke(IMessage msg)
{
IMethodCallMessage callMsg = msg as IMethodCallMessage; //调用前拦截;
BeforeInvoke(callMsg.MethodBase); try { //调用真实方法;
object retValue = callMsg.MethodBase.Invoke(_realObject, callMsg.Args); return new ReturnMessage(retValue, callMsg.Args, callMsg.ArgCount - callMsg.InArgCount, callMsg.LogicalCallContext, callMsg);
} catch (Exception ex)
{ return new ReturnMessage(ex, callMsg);
} finally { //调用后处理;
AfterInvoke(callMsg.MethodBase);
}
}private void BeforeInvoke(MethodBase method)
{
Console.WriteLine(“Before Invoke {0}::{1}”, typeof(T).FullName, method.ToString());
}private void AfterInvoke(MethodBase method)
{
Console.WriteLine(“After Invoke {0}::{1}”, typeof(T).FullName, method.ToString());
}
}
以上的代码 AopProxy
我们为上面的 DataObject 添加一个新的工厂方法 CreateDataObject2 用通过 AopProxy
使用 AopProxy 创建透明代理
///
public static IDataObject CreateDataObjec2(int p1, int p2)
{ //创建真实实例;
DataObject realObject = new DataObject();
realObject._parameter1 = p1;
realObject._parameter2 = p2;//创建真实代理;
AopProxy<IDataObject> proxy = new AopProxy<IDataObject>(realObject);//返回透明代理;
return (IDataObject)proxy.GetTransparentProxy();
}
修改前面的入口程序通过 CreateDataObject2 方法获得代理,如下:
public static void Main(string[] args)
{
IDataObject dtObj = DataObject.CreateDataObject2(10, 6); int result = dtObj.Compute();
Console.ReadLine();
}
此次的输出如下:
实现我们预期的结果:通过 AopProxy
至此似乎一切都很完美,但是千万别以为世上真的存在“完美”。基于RealProxy 的实现是有其局限性的。在上面的 AopProxy
此外,在 AopProxy
AOP的实现很简单吧。如果我们再进一步,利用自定义 Attribute 对类型的方法进行标记 Before 和 After 操作,在 AopProxy