[原创]Enterprise Library Policy Injection Application Block 之二: PIAB设计和实现原理

前面一篇文章中,我对Enterprise Library中的PIAB Policy Injection Application Block)作了简单的介绍。在这篇文章主要谈谈我个人对PIAB设计和实现原理的一些理解。在介绍过程中,我尽量采用由浅入深出的方式,同时结合例子、Source Code。希望通过本篇文章让大家对PIAB有一个全面、深刻的认识。

一、MBRObjRefRealProxyTransparentProxy

在真正进入PIAB之前,我们现来谈论一些与之相关的、必要的背景知识。MBRObjRefRealProxyTransparentProxy,对于这些名词,我想熟悉或者接触过.NET Remoting的人肯定不会不陌生。由于PIAB的实现机制依赖于Remoting的这种Marshaling,如果对此不了解的读者将对后面的介绍很难理解,所以很有必要先做以下简单的介绍。

我们知道,CLR通过AppDomain实现不同Application之间的隔离,在通常情况下,不同AppDomain不同共享内存。在一个AppDomain中创建的对象不能直接被运行在另一个AppDomain的程序使用。跨AppDomain对象的共享依赖于一个重要的过程:Marshaling。我们有两种不同的Marshaling方式:Marshaling by ValueMarshaling by Reference。前者采用的是Serialization/Deserialization的方式,而后者则是采用传递Reference的方式来实现,其实现原来如下:

Remoting Infrastructure先生成对象的ObjRef InstanceObjRefSystem.Runtime.Remoting.ObjRef)代表远程对象的一个Reference,存储着跨AppDomain远程调用所需的所有的信息,比如URI、类型的层次结构、以及实现的Interface等等。ObjRef是可以序列化的,也就是说它可以按照by Value的方式进行Marshaling。所以可以这么说Marshaling by Reference依赖对对ObjRefMarshaling by Value

ObjRef产地到另一个AppDomain的实现,将根据ObjRef的数据生成两个Proxy对象:RealProxyTransparentProxyRealProxy根据ObjRef创建,通过RealProxy创建TransparentProxy。当进行远程调用的时候,Client直接和TransparentProxy打交道,对TransparentProxy的调用将会ForwardRealProxyRealProxy通过Remoting InfrastructureCommunicateActivation机制将Invocate传递给被激活的Remote Object

MBR通常在一个跨AppDomain的环境下实现远程调用,但是这种机制在用一个AppDomian依然有效,而且可以免去跨AppDomain的性能损失。PIAB就是充分运用了这种在同一个AppDomain下的MBR

二、Method Interception & Custom RealProxy

在第一部分我们知道,PIAB的实现是通过将Policy应用到对应的Method上,在真正执行目标对象具体的方法的之前,PIAB将整个方法的调用拦截,然后逐个调用应在Method上的Policy包含的所有CallHandler(在前一章我们说过Policy = Matching Rule + Call Handler),最后再调用真正目标对象的方法。我们把这种机制成为Method Injection

如何才有实现这样的Method Injection呢?这就要需要使用到我们在上面一节介绍的MBR了。通过上面的介绍,我们知道我们调用一个MBR Object的过程是这样的:

Client Code==〉Transparent Proxy==〉Real Proxy==〉Target Object

在上面的Invocate Chain中,由于真正的目标对象的方法在最后一部才被调用,我们完全有可以在中途将调用劫持,使之先调用我们的CallHandler。而这种Inject的突破口在于RealProxy。在FCLFramework Class Library)中RealProxySystem.Runtime.Remoting.Proxies.RealProxy)是个特殊的Abstract Class,你可以继承RealProxy定义你自己的Custom RealProxy,将你需要注入的操作写在Invoke方法中。PIAB采用的就是这样一种解决方案。

我们先不忙介绍PIAB的具体的实现原理,因为相对比较复杂。为了使读者能够更加容易地理解PIAB的实现,我写了一个简单的例子。我们它能够大体体现PIAB的实现机制。

这是一个简单的Console Application,我首先定义了一个Custom RealProxy

publicclass MyRealProxy<T>:RealProxy

{

private T _target;

public MyRealProxy(T target)

: base(typeof(T))

{

this._target = target;

}

public override IMessage Invoke(IMessage msg)

{

//Invoke injected pre-operation.

Console.WriteLine("The injected pre-operation is invoked");

//Invoke the real target instance.

IMethodCallMessage callMessage = (IMethodCallMessage)msg;

object returnValue = callMessage.MethodBase.Invoke(this._target, callMessage.Args);

//Invoke the injected post-operation.

Console.WriteLine("The injected post-peration is executed");

//Return

return new ReturnMessage(returnValue, new object[0], 0, null, callMessage);

}
}

这是一个GenericRealProxy。在Invoke方法中,两个Console.Write()代表PIAB注入的CallHandler的调用(对于CallHandler的操作可以是在调用Target Object之前调用,也可以在之后调用,我们不妨称这两种类型的操作为Pre-operationPost-op

eration)。而对Target object的调用实际上是通过Reflection的方式调用的(callMessage.MethodBase.Invoke)。MyRealProxy通过传入Target Instance来创建。

我们在创建一个Factory Class,用于创建TransparentProxy。在PIAB中,这样一个ClassMicrosoft.Practices.EnterpriseLibrary.PolicyInjection.PolicyInjection来充当。

public static class PolicyInjectionFactory

{

public static T Create<T>()

{

T instance = Activator.CreateInstance<T>();

MyRealProxy<T> realProxy = new MyRealProxy<T>(instance);

T transparentProxy = (T)realProxy.GetTransparentProxy();

return transparentProxy;

}
}

先通过Reflection的方式来创建Target Instance。通过该Instance创建我们在上面定义的Custom RealProxy。最后通过RealProxy返回一个TransparentProxy

有了上面两个Class,我们的编写如下的Code来验证我们自己的Method Injection

public class Foo:MarshalByRefObject

{

public voidDoSomeThing()

{

Console.WriteLine("The method of target object is invoked!");

}
}

public class Program

{

public static void Main()

{

Foo foo = PolicyInjectionFactory.Create<Foo>();

foo.DoSomeThing();

}
}

我们来看看程序运行后的结果:

[原创]Enterprise Library Policy Injection Application Block 之二: PIAB设计和实现原理
可以看到正式我们需要的结果。从这个例子中我们可以看到,我们的
Client code中包含的仅仅是Business Logic相关的code, 而另外一些业务无关的Code则是通过Custom RealProxy的形式注入到Invocation Stack中。这充分体现了Business ConcernCrosscutting Concern的分离。

三、Call Handler Pipeline

我想有了上面的理论和例子作基础,大家对于PIAB真正的实现不难理解了。我们先来介绍一下PI一个重要的概念:CallHandler Pipeline。我们知道Policy被运用Method方面,一个Policy有多个CallHandler。所有一个Method往往关联着一连串的CallHandler。这种现在很常见,就上我们第一部分给出的例子一样,一个简单的ProcessOrder方面需要执行许多额外的业务无关的逻辑:AuthorizationAuditingTransaction EnlistException HandlingLogging

PIAB中,这些基于某个Method的所有CallHandler逐个串联在一起,组成一个CallHandler Pipeline。具体情况如下图:

[原创]Enterprise Library Policy Injection Application Block 之二: PIAB设计和实现原理
我们从
Class Diagram的角度来认识CallHandler Pipeline(在PIAB中通过Microsoft.Practices.EnterpriseLibrary.PolicyInjection.HandlerPipeline来表示)。

[原创]Enterprise Library Policy Injection Application Block 之二: PIAB设计和实现原理

public delegate IMethodReturnInvokeHandlerDelegate(IMethodInvocation input, GetNextHandlerDelegate getNext);

public interface IMethodInvocation

{

// Methods

IMethodReturnCreateExceptionMethodReturn(Exception ex);

IMethodReturnCreateMethodReturn(object returnValue, params object[] outputs);

// Properties

IParameterCollectionArguments { get; }

IParameterCollectionInputs { get; }

IDictionaryInvocationContext { get; }

MethodBaseMethodBase { get; }

objectTarget { get; }
}
public delegate InvokeHandlerDelegate GetNextHandlerDelegate();

public interface ICallHandler

{

// Methods

IMethodReturnInvoke(IMethodInvocation input, GetNextHandlerDelegate getNext);
}

IMethodInvocation代表对一个方法的调用,它封装了一个Method Invocation的相关信息。比如:Parameter ListInvocation ContextTarget Object.InvokeHandlerDelegate代表的是对CallHandler的调用,由于所有的CallHandler被串成一个CallHandler Pipeline,在调用了当前CallHandler之后,需要调用Pipeline中后一个CallHandler,对后一个CallHandler调用通过一个DelegateGetNextHandlerDelegate来表示,而该Delegate的返回值是InvokeHandlerDelegate。结合上面的CallHandler Pipeline的链状结构,对这些应该不难理解。

我们最后来看看HandlerPipeline的定义,它的所有的CallHandler通过一个List来表示。

public class HandlerPipeline

{

// Fields

private List<ICallHandler> handlers;

// Methods

public HandlerPipeline();

public HandlerPipeline(IEnumerable<ICallHandler> handlers);

public IMethodReturnInvoke(IMethodInvocation input, InvokeHandlerDelegate target);
}

HandlerPipeline通过Invoke开始对其CallHandler PipeLine的调用:

public IMethodReturn Invoke(IMethodInvocation input, InvokeHandlerDelegate target)

{

if (this.handlers.Count == 0)

{

return target(input, null);

}

int handlerIndex = 0;

return this.handlers[0].Invoke(input, delegate {

handlerIndex++;

if (handlerIndex < this.handlersfont-size

你可能感兴趣的:(数据结构,Microsoft)