本文实例选自《设计模式--基于c#的工程化实现及扩展》
原文的目的是通过.NET对AOP机制的原生支持实现Decorator模式。
基础原理(摘自Artech 的 Enterprise Library Policy Injection Application Block 之二: PIAB设计和实现原理):
MBR、ObjRef、RealProxy和TransparentProxy,对于这些名词,我想熟悉或者接触过.NET Remoting的人肯定不会不陌生。由于PIAB的实现机制依赖于Remoting的这种Marshaling,如果对此不了解的读者将对后面的介绍很难理解,所以很有必要先做以下简单的介绍。
我们知道,CLR通过AppDomain实现不同Application之间的隔离,在通常情况下,不同AppDomain不同共享内存。在一个AppDomain中创建的对象不能直接被运行在另一个AppDomain的程序使用。跨AppDomain对象的共享依赖于一个重要的过程:Marshaling。我们有两种不同的Marshaling方式:Marshaling by Value和Marshaling by Reference。前者采用的是Serialization/Deserialization的方式,而后者则是采用传递Reference的方式来实现,其实现原来如下:
Remoting Infrastructure先生成对象的ObjRef Instance,ObjRef(System.Runtime.Remoting.ObjRef)代表远程对象的一个Reference,存储着跨AppDomain远程调用所需的所有的信息,比如URI、类型的层次结构、以及实现的Interface等等。ObjRef是可以序列化的,也就是说它可以按照by Value的方式进行Marshaling。所以可以这么说Marshaling by Reference依赖对对ObjRef的Marshaling by Value。
当ObjRef产地到另一个AppDomain的实现,将根据ObjRef的数据生成两个Proxy对象:RealProxy和TransparentProxy。RealProxy根据ObjRef创建,通过RealProxy创建TransparentProxy。当进行远程调用的时候,Client直接和TransparentProxy打交道,对TransparentProxy的调用将会Forward到RealProxy,RealProxy通过Remoting Infrastructure的Communicate和Activation机制将Invocate传递给被激活的Remote Object。
MBR通常在一个跨AppDomain的环境下实现远程调用,但是这种机制在用一个AppDomian依然有效,而且可以免去跨AppDomain的性能损失。PIAB就是充分运用了这种在同一个AppDomain下的MBR。
下面这个简单AOP 的例子也是使用了以上思想。通过在RealProxy中增加对目标方法调用的拦截。然后将调用的处理权交由目标方法上的标签类进行处理。在标签类处理完各种检验后RealProxy再执行对目标方法的调用。
代码如下:
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property)]
public abstract class DecoratorAttributeBase : Attribute
{
public abstract void Intercept(object target);
}
/// <summary>
/// 实际代理对象
/// </summary>
public class CustomProxy<T> : RealProxy, IDisposable where T : MarshalByRefObject
{
/// <summary>
/// 构造过程中把Proxy需要操作的内容与实际目标对象实例Attach到一起
/// </summary>
/// <param name="target"></param>
public CustomProxy(T target):base(target.GetType())
{
AttachServer(target);
}
#region IDisposable 成员
/// <summary>
/// 析构过程则借助Proxy和目标对象实例的Attach,便于GC回收
/// </summary>
public void Dispose()
{
DetachServer();
}
#endregion
/// <summary>
/// 创建目标实例的transparent代理对象
/// </summary>
/// <param name="target"></param>
/// <returns></returns>
public static T Create(T target)
{
if (target == null)
{
throw new ArgumentNullException("target");
}
return (T)(new CustomProxy<T>(target).GetTransparentProxy());
}
public override IMessage Invoke(IMessage msg)
{
MethodCallMessageWrapper caller = new MethodCallMessageWrapper((IMethodCallMessage)msg);
//提取实际宿主对象
MethodInfo method = (MethodInfo)caller.MethodBase;
T target = (T)GetUnwrappedServer();
DecoratorAttributeBase[] attributes =
(DecoratorAttributeBase[])method.GetCustomAttributes(typeof(DecoratorAttributeBase), true);
if (attributes.Length > 0)
{
foreach (DecoratorAttributeBase attribute in attributes)
{
attribute.Intercept(caller);
}
}
object ret = method.Invoke(target,caller.Args);
return new ReturnMessage(ret, caller.Args, caller.ArgCount, caller.LogicalCallContext, caller);
}
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class ArgumentNotEmptyAttribute : DecoratorAttributeBase
{
public override void Intercept(object target)
{
MethodCallMessageWrapper caller = new MethodCallMessageWrapper((IMethodCallMessage)target);
if (caller.ArgCount == 0)
{
return;
}
foreach (object arg in caller.Args)
{
if ((string.IsNullOrEmpty((string)arg)))
{
throw new ArgumentException("string is null or empty");
}
}
}
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class ArgumentTypeRestrictionAttribute : DecoratorAttributeBase
{
private Type type;
public ArgumentTypeRestrictionAttribute(Type type)
{
this.type = type;
}
public override void Intercept(object target)
{
MethodCallMessageWrapper caller = new MethodCallMessageWrapper((IMethodCallMessage)target);
if (caller.ArgCount == 0)
{
return;
}
for (int i = 0; i < caller.ArgCount; i++)
{
object arg = caller.Args[i];
if ((arg.GetType() != this.type) && (!arg.GetType().IsAssignableFrom(this.type)))
{
throw new ArgumentException(i.ToString());
}
}
}
}
class User : MarshalByRefObject
{
private string name;
private string title;
[ArgumentTypeRestriction(typeof(string))] // 提供拦截入口
[ArgumentNotEmpty()] // 提供拦截入口
public void SetUserInfo(object name, object title)
{
this.name = (string)name;
this.title = (string)title;
}
}
[TestMethod]
public void TestCSharpAOP()
{
User user = CustomProxy<User>.Create(new User());
user.SetUserInfo("joe","manager");
try
{
user.SetUserInfo(20, "manager");
}
catch (Exception ex)
{
Assert.AreEqual<string>("0", ex.Message);
}
try
{
user.SetUserInfo("", "manager");
}
catch (Exception ex)
{
Assert.AreEqual<string>("string is null or empty", ex.Message);
}
}
收录这段代码只是为了了解下.NET下实现AOP机制的基本方式。如果想在项目中使用AOP机制,我觉得还是使用Enterprise Lirbrary 的PIAB吧。
自己实现AOP的框架需没有太大必要了。
另外书中还列举了一种使用MSIL的实现方式。感觉比较复杂没有太深入。
本文有点炒剩饭。。。纯属个人备忘