Policy Injection Application Block 设计和实现原理
在Policy Injection Application Block简介中,对Enterprise Library中的PIAB (Policy Injection Application Block)作了简单的介绍。在这篇文章主要谈谈我个人对PIAB设计和实现原理的一些理解。
一、MBR、ObjRef、RealProxy、TransparentProxy
在真正进入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。
二、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。在FCL(Framework Class Library)中RealProxy(System.Runtime.Remoting.Proxies.RealProxy)是个特殊的Abstract Class,你可以继承RealProxy定义你自己的Custom RealProxy,将你需要注入的操作写在Invoke方法中。PIAB采用的就是这样一种解决方案。
我们先不忙介绍PIAB的具体的实现原理,因为相对比较复杂。为了使读者能够更加容易地理解PIAB的实现,我写了一个简单的例子。我们它能够大体体现PIAB的实现机制。这是一个简单的Console Application,我首先定义了一个Custom RealProxy:
1: public class MyRealProxy<T> : RealProxy 2: { 3: private T _target; 4: public MyRealProxy(T target) 5: : base(typeof(T)) 6: { 7: this._target = target; 8: } 9: 10: public override IMessage Invoke(IMessage msg) 11: { 12: //Invoke injected pre-operation. 13: Console.WriteLine("The injected pre-operation is invoked"); 14: //Invoke the real target instance. 15: IMethodCallMessage callMessage = (IMethodCallMessage)msg; 16: object returnValue = callMessage.MethodBase.Invoke(this._target, callMessage.Args); 17: //Invoke the injected post-operation. 18: Console.WriteLine("The injected post-peration is executed"); 19: //Return 20: return new ReturnMessage(returnValue, new object[0], 0, null, callMessage); 21: } 22: }
这是一个Generic的RealProxy。在Invoke方法中,两个Console.Write()代表PIAB注入的CallHandler的调用(对于CallHandler的操作可以是在调用Target Object之前调用,也可以在之后调用,我们不妨称这两种类型的操作为Pre-operation和Post-operation)。而对Target object的调用实际上是通过Reflection的方式调用的(callMessage.MethodBase.Invoke)。MyRealProxy通过传入Target Instance来创建。
我们在创建一个Factory Class,用于创建TransparentProxy。在PIAB中,这样一个Class由Microsoft.Practices.EnterpriseLibrary.PolicyInjection.PolicyInjection来充当。
1: public static class PolicyInjectionFactory 2: { 3: public static T Create<T>() 4: { 5: T instance = Activator.CreateInstance<T>(); 6: MyRealProxy<T> realProxy = new MyRealProxy<T>(instance); 7: T transparentProxy = (T)realProxy.GetTransparentProxy(); 8: return transparentProxy; 9: } 10: }
先通过Reflection的方式来创建Target Instance。通过该Instance创建我们在上面定义的Custom RealProxy。最后通过RealProxy返回一个TransparentProxy。
有了上面两个Class,我们的编写如下的Code来验证我们自己的Method Injection:
1: public class Foo : MarshalByRefObject 2: { 3: public void DoSomeThing() 4: { 5: Console.WriteLine("The method of target object is invoked!"); 6: } 7: } 8: 9: public class Program 10: { 11: public static void Main() 12: { 13: Foo foo = PolicyInjectionFactory.Create<Foo>(); 14: foo.DoSomeThing(); 15: } 16: }
我们来看看程序运行后的结果:
可以看到正式我们需要的结果。从这个例子中我们可以看到,我们的Client code中包含的仅仅是Business Logic相关的code, 而另外一些业务无关的Code则是通过Custom RealProxy的形式注入到Invocation Stack中。这充分体现了Business Concern和Crosscutting Concern的分离。
三、Call Handler Pipeline
我想有了上面的理论和例子作基础,大家对于PIAB真正的实现不难理解了。我们先来介绍一下PI一个重要的概念:CallHandler Pipeline。我们知道Policy被运用Method方面,一个Policy有多个CallHandler。所有一个Method往往关联着一连串的CallHandler。这种现在很常见,就上我们第一部分给出的例子一样,一个简单的ProcessOrder方面需要执行许多额外的业务无关的逻辑:Authorization、Auditing、Transaction Enlist、Exception Handling和Logging。
在PIAB中,这些基于某个Method的所有CallHandler逐个串联在一起,组成一个CallHandler Pipeline 。具体情况如下图:
我们从Class Diagram的角度来认识CallHandler Pipeline (在PIAB中通过Microsoft.Practices.EnterpriseLibrary.PolicyInjection.HandlerPipeline来表示)。
1: public delegate IMethodReturnInvokeHandlerDelegate(IMethodInvocation input, GetNextHandlerDelegate getNext); 2: public interface IMethodInvocation 3: { 4: IMethodReturnCreateExceptionMethodReturn(Exception ex); 5: IMethodReturnCreateMethodReturn(object returnValue, params object[] outputs); 6: IParameterCollectionArguments { get; } 7: IParameterCollectionInputs { get; } 8: IDictionaryInvocationContext { get; } 9: MethodBaseMethodBase { get; } 10: objectTarget { get; } 11: } 12: public delegate InvokeHandlerDelegate GetNextHandlerDelegate(); 13: public interface ICallHandler 14: { 15: IMethodReturnInvoke(IMethodInvocation input, GetNextHandlerDelegate getNext); 16: }
IMethodInvocation代表对一个方法的调用,它封装了一个Method Invocation的相关信息。比如:Parameter List、Invocation Context和Target Object. InvokeHandlerDelegate代表的是对CallHandler的调用,由于所有的CallHandler被串成一个CallHandler Pipeline ,在调用了当前CallHandler之后,需要调用Pipeline中后一个CallHandler,对后一个CallHandler调用通过一个Delegate,GetNextHandlerDelegate来表示,而该Delegate的返回值是InvokeHandlerDelegate。结合上面的CallHandler Pipeline 的链状结构,对这些应该不难理解。
我们最后来看看HandlerPipeline的定义,它的所有的CallHandler通过一个List来表示。
1: public class HandlerPipeline 2: { 3: private List<ICallHandler> handlers; 4: public HandlerPipeline(); 5: public HandlerPipeline(IEnumerable<ICallHandler> handlers); 6: public IMethodReturnInvoke(IMethodInvocation input, InvokeHandlerDelegate target); 7: }
HandlerPipeline通过Invoke开始对其CallHandler PipeLine的调用:
1: public IMethodReturn Invoke(IMethodInvocation input, InvokeHandlerDelegate target) 2: { 3: if (this.handlers.Count == 0) 4: { 5: return target(input, null); 6: } 7: int handlerIndex = 0; 8: return this.handlers[0].Invoke(input, delegate { 9: handlerIndex++; 10: if (handlerIndex < this.handlers.Count) 11: { 12: ICallHandler local1 = this.handlers[handlerIndex]; 13: return new InvokeHandlerDelegate(local1.Invoke); 14: } 15: return target; 16: }); 17: }
逻辑并不复杂,按照CallHandler List的先后顺序逐个调用,最后调用Target Object。方法中的第二个参数代表target代表对Target Object的调用。
四、PIAB中的Custom RealProxy:InterceptingRealProxy
我们一直再强调,PIAB实际上是通过自定义RealProxy来实现的。而且在第二节我们也实验了这种做法的可行性。我们现在就来看看PIAB的Custom RealProxy:Microsoft.Practices.EnterpriseLibrary.PolicyInjection.RemotingInterception.InterceptingRealProxy。
1: public class InterceptingRealProxy : RealProxy, IRemotingTypeInfo 2: { 3: private Dictionary<MethodBase, HandlerPipeline> memberHandlers; 4: private readonly object target; 5: private string typeName; 6: 7: public InterceptingRealProxy(object target, Type classToProxy, PolicySet policies); 8: private void AddHandlersForInterface(Type targetType, Type itf); 9: private void AddHandlersForType(Type classToProxy, PolicySet policies); 10: public bool CanCastTo(Type fromType, object o); 11: public override IMessage Invoke(IMessage msg); 12: public object Target { get; } 13: public string TypeName { get; set; } 14: }
上面是它所有的成员定义,其中memberHandlers是一个以MethodBase为Key的Dictionary,表示应用在Target Object上面的所有的CallHandler Pipeline。通过这个很容易获得某个具体的方法调用的Pipeline。而target值得是真正的Target Object。我们着重来看看Invoke的定义:
1: public override IMessage Invoke(IMessage msg) 2: { 3: HandlerPipeline pipeline; 4: IMethodCallMessage callMessage = (IMethodCallMessage) msg; 5: if (this.memberHandlers.ContainsKey(callMessage.MethodBase)) 6: { 7: pipeline = this.memberHandlers[callMessage.MethodBase]; 8: } 9: else 10: { 11: pipeline = new HandlerPipeline(); 12: } 13: RemotingMethodInvocation invocation = new RemotingMethodInvocation(callMessage, this.target); 14: return ((RemotingMethodReturn) pipeline.Invoke(invocation, delegate (IMethodInvocation input, GetNextHandlerDelegate getNext) { 15: try 16: { 17: object returnValue = callMessage.MethodBase.Invoke(this.target, invocation.Arguments); 18: return input.CreateMethodReturn(returnValue, invocation.Arguments); 19: } 20: catch (TargetInvocationException exception) 21: { 22: return input.CreateExceptionMethodReturn(exception.InnerException); 23: } 24: 25: })).ToMethodReturnMessage(); 26: }
上面的一段代码不长,多看几遍应该不难理解。总的来说上面的Code现根据msg解析出MethodBase,再获取对应的CallHandler Pipeline,最后调用Pipeline。
五、Policy Injection Transparent Proxy Factory
介绍到这里,细心的读者可以意识到了,我们实际上还还有两件事情没有解决:CallHandler Pipeline的初始化和Transparent Proxy的创建。这两件事情都由PolicyInject.Create和方法来完成。
需要指出的,应用PIAB的Class需要具有两个条件中至少一个:
-
Class继承System.MarshalByRefObject。
-
Class实现某个Interface。
PolicyInjectiond提供了两种类型的Create方式,一种需要制定Interface,另一种不需要:
1: public static TInterface Create<TObject, TInterface>(params object[] args); 2: public static TObject Create<TObject>(params object[] args);
其实还有两个重载,在这里就不需要多做介绍了。在具体的实现中,最终又是调用一个PolicyInjector对象来实现的。
1: public static TObject Create<TObject>(params object[] args) 2: { 3: return DefaultPolicyInjector.Create<TObject>(args); 4: } 5: 6: public static TInterface Create<TObject, TInterface>(params object[] args) 7: { 8: 9: returnDefaultPolicyInjector.Create<TObject, TInterface>(args); 10: }
PolicyInjector是一个Abstract Class。其中Policies属性代表应用在该对象上的所有Policy(Policy = CallHandler + Matching Rule)
1: [CustomFactory(typeof(PolicyInjectorCustomFactory))] 2: public abstract class PolicyInjector 3: { 4: private PolicySetpolicies; 5: public PolicyInjector(); 6: public PolicyInjector(PolicySet policies); 7: public TInterface Create<TObject, TInterface>(params object[] args); 8: public TObject Create<TObject>(params object[] args); 9: public objectCreate(Type typeToCreate, params object[] args); 10: public object Create(Type typeToCreate, Type typeToReturn, params object[] args); 11: public PolicySetPolicies { get; set; } 12: //Otehrs 13: }
在PolicyInjection Class中定义了一个叫做DefaultPolicyInjector的属性,其定义如下:
1: private static PolicyInjectorDefaultPolicyInjector 2: { 3: get 4: { 5: if (defaultPolicyInjector == null) 6: { 7: lock (singletonLock) 8: { 9: if (defaultPolicyInjector == null) 10: { 11: defaultPolicyInjector = GetInjectorFromConfig(ConfigurationSourceFactory.Create()); 12: } 13: } 14: } 15: return defaultPolicyInjector; 16: } 17: }
由于上面这个方法具体调用的Stack trace太深了,不可能很详细地指出其具体的实现。简单地说,该属性会返回一个默认的PolicyInjector:RemotingPolicyInjector,并对其进行初始化。初始化的内容就包括初始化所有的Policy(这就是我们在本节开始提出的CallHandler Pipeline的初始化)。由于我们可以有两种方式将Policy映射到目标成员:Attribute和Configuration。所有具体的做法是先通过分析Configuration找出所有通过configuration方式添加的Policy,然后通过Reflection找出所有通过使用Attribute方式添加的Policy。所以,如果你通过两种方式将相同的Policy应用到同一个对象上,该对象上将会有两个一样CallHandler,个人觉得这是一个值得商榷的做法,我不太赞成这样的行为。
我们接着看PolicyInjector接着是如何工作的:
1: public TInterface Create<TObject, TInterface>(params object[] args) 2: { 3: return (TInterface) this.Create(typeof(TObject), typeof(TInterface), args); 4: } 5: 6: public TObject Create<TObject>(params object[] args) 7: { 8: return (TObject) this.Create(typeof(TObject), typeof(TObject), args); 9: }
上面连个方法由于调用到同一个Create Overload:
1: publicobjectCreate(Type typeToCreate, Type typeToReturn, params object[] args) 2: { 3: PolicySet policiesFor = this.policies.GetPoliciesFor(typeToCreate); 4: this.EnsureTypeIsInterceptable(typeToReturn, policiesFor); 5: return this.DoCreate(typeToCreate, typeToReturn, policiesFor, args); 6: }
首先找出对应Type的Policy,然后判断该类型是否支持Method Interception。RemotingPolicyInjector对该方法是这样实现的:要么继承MarshalByRefObject的Class,要么是个Interface。所以我们才有本节开始提出的两个条件。
1: public override boolTypeSupportsInterception(Type t) 2: { 3: if (!typeof(MarshalByRefObject).IsAssignableFrom(t)) 4: { 5: return t.IsInterface; 6: } 7: return true; 8: }
上面连个方法由于调用到同一个Create Overload:
1: public object Create(Type typeToCreate, Type typeToReturn, params object[] args) 2: { 3: PolicySet policiesFor = this.policies.GetPoliciesFor(typeToCreate); 4: this.EnsureTypeIsInterceptable(typeToReturn, policiesFor); 5: return this.DoCreate(typeToCreate, typeToReturn, policiesFor, args); 6: }
首先找出对应Type的Policy,然后判断该类型是否支持Method Interception。RemotingPolicyInjector对该方法是这样实现的:要么继承MarshalByRefObject的Class,要么是个Interface。所以我们才有本节开始提出的两个条件。
1: protected override object DoWrap(object instance, Type typeToReturn, PolicySet policiesForThisType) 2: { 3: if (PolicyInjector.PolicyRequiresInterception(policiesForThisType)) 4: { 5: InterceptingRealProxy proxy = new InterceptingRealProxy(this.UnwrapTarget(instance), typeToReturn, policiesForThisType); 6: return proxy.GetTransparentProxy(); 7: } 8: return instance; 9: }
和我们给出的例子差不多,创建RealPoxy,根据该RealProxy返回Transparent Proxy。
五、Policy Injection Design
最后给出整个PIAB实现的所使用的Class,基本上所有的Class都在上面的内容中介绍过了:
在本系列的第三部分,我将介绍如何创建Custom Handler,以及如何将他采用不同的方式应用到你的Application中。
作者:Artech
出处:http://artech.cnblogs.com/
加支付宝好友偷能量挖...