上篇文章我说到了在代码中可以利用泛型委托来封装异常处理,这样可以让程序看起来更加清晰,要想完成功能需要调用者调用指定的工厂方法才行,但要想改变某些程序员的编码习惯我想是一件比较困难的事情。有朋友说利用委托来实现异常处理并不算是真正意义上的AOP,因为传统的AOP并不需要客户端做代码结构的变更,最多也就是配置上的问题。但在.net中要想实现AOP,我想最方便的实现机制要属代理机制了,但只要利用代理,在性能上就会造成一定的影响。
如果开发过分布式服务,像remotion,wcf等,消息都是它们通信的重要手段。客户端通过方法调用形式体现的服务访问需要转换成具体的消息,然后经过编码才能利用传输通道发送给服务端,服务执行的结果也只能以消息的形式返回给调用方。
这些分布式服务有一共同特点:都通过代理方法间接的调用服务。服务代理,它自身并不提供服务的实现,只是起到一个中介作用,客户端把服务请求发送给服务代理,服务代理再去调真正的服务,同样服务返回时,也是返回给服务代理,再由服务代理返回给客户端。看到这,我想对于实现AOP的拦截就有点眉目了。在.net中,我们可以写自定义的RealProxy来实现AOP的方法拦截功能。
服务代理通常又分为以下两种:
1:透明代理。客户端在跨任何类型的远程处理边界使用对象时,对对象使用的实际上是透明代理。透明代理使人以为实际对象驻留在客户端空间中。它实现这一点的方法是:使用远程处理基础结构将对其进行的调用转发给真实对象。透明代理本身由 RealProxy 类型的托管运行时类的实例收容。RealProxy 实现从透明代理转发操作所需的部分功能。代理对象继承托管对象(例如垃圾回收、对成员和方法的支持)的关联语义,可以将其进行扩展以形成新类。这样,该代理具有双重性质,一方面,它需要充当与远程对象(透明代理)相同的类的对象;另一方面,它本身是托管对象。
2:真实代理。RealProxy来实现与远程服务进行通信,所以这里就是我们实现AOP的地方。
下图是透明代理与真实代理以及远程对象的调用关系图:
下图是利用自定义的RealProxy实现AOP方法拦截的原理图:
自定义异常代理类:
说明:1>自定义的代理类需要继承RealProxy。
2>从 RealProxy 继承时,必须重写 Invoke方法。
3>下面代码中的LogManage是一个log4net接口,我们可以把异常统一记录到日志中,供日后分析。
代码
/// <summary>
/// Aspect代理,在这个类里面,实现对方法的拦截
/// </summary>
public class AspectProxyErrorLog : RealProxy
{
AspectManagedAttribute attr;
/// <summary>
/// 默认构造函数
/// </summary>
public AspectProxyErrorLog() : base()
{
}
/// <summary>
/// 构造函数
/// </summary>
/// <param name="myType">被代理的类的类型</param>
public AspectProxyErrorLog(Type myType) : base(myType)
{
}
/// <summary>
/// 构造函数
/// </summary>
/// <param name="myType">被代理的类的类型</param>
/// <param name="obj">被代理的对象</param>
public AspectProxyErrorLog(Type myType,MarshalByRefObject obj) : base(myType)
{
target=obj;
}
MarshalByRefObject target;
ILog LogManage;
/// <summary>
/// 当在派生类中重写时,在当前实例所表示的远程对象上调用在所提供的 IMessage 中指定的方法。<br />
/// WebsharpAspect在这里执行对方法执行的拦截处理
/// </summary>
/// <param name="msg">IMessage,包含有关方法调用的信息。</param>
/// <returns>调用的方法所返回的消息,包含返回值和所有 out 或 ref 参数。</returns>
public override IMessage Invoke(IMessage msg)
{
IMessage retMsg=null ;
IMethodCallMessage methodCall = (IMethodCallMessage)msg;
IMethodReturnMessage methodReturn = null;
object[] copiedArgs = Array.CreateInstance(typeof(object), methodCall.Args.Length) as object[];
methodCall.Args.CopyTo(copiedArgs, 0);
object[] attrs = null;
CoustomerErrorHandleAttribute ceha = null;
if (msg is IConstructionCallMessage)
{
IConstructionCallMessage ccm
= (IConstructionCallMessage)msg;
RemotingServices.GetRealProxy(target).InitializeServerObject(ccm);
ObjRef oRef = RemotingServices.Marshal(target);
RemotingServices.Unmarshal(oRef);
retMsg = EnterpriseServicesHelper.CreateConstructionReturnMessage(ccm, (MarshalByRefObject)this.GetTransparentProxy());
}
else
{
IMethodCallMessage mcm = (IMethodCallMessage)msg;
attrs = methodCall.MethodBase.GetCustomAttributes(typeof(CoustomerErrorHandleAttribute), false);
ceha = LogManagerFactory.GetCoustomerErrorHandleAttribute(attrs, methodCall.MethodBase.Name );
if (null != ceha)
{
LogManage = ceha.ILogName;
}
try
{
object returnValue = methodCall.MethodBase.Invoke(this.target, copiedArgs);
methodReturn = new ReturnMessage(returnValue, copiedArgs, copiedArgs.Length, methodCall.LogicalCallContext, methodCall);
}
catch (Exception ex)
{
if (null != ex.InnerException)
{
methodReturn = new ReturnMessage(ex.InnerException, methodCall);
}
else
{
methodReturn = new ReturnMessage(ex, methodCall);
}
}
retMsg = methodReturn;
}
if (null != methodReturn)
{
if (null != methodReturn.Exception )
{
if (null != this.LogManage )
{
this.LogManage.Error(ceha .MethodErrorText + methodReturn.Exception.ToString());
}
}
}
return retMsg;
}
}
上面只是贴了部分代码,在下一篇中,我会对这部分代码做更加详细的分析。