程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> Enterprise Library Policy Injection Application Block之二

Enterprise Library Policy Injection Application Block之二

編輯:關於.NET

在前面一篇文章中,我對Enterprise Library中的PIAB (Policy Injection Application Block)作了簡單的介紹。在這篇文章主要談談我個人對PIAB設計和實現原理的一些理解。在介紹過程中,我盡量采用由淺入深出的方式,同時結合例子、Source Code。希望通過本篇文章讓大家對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:

public class 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);
}
}

這是一個Generic的RealProxy。在Invoke方法中,兩個Console.Write()代表PIAB注入的CallHandler的調用(對於CallHandler的操作可以是在調用Target Object之前調用,也可以在之後調用,我們不妨稱這兩種類型的操作為Pre-operation和Post-op

eration)。而對Target object的調用實際上是通過Reflection的方式調用的(callMessage.MethodBase.Invoke)。MyRealProxy通過傳入Target Instance來創建。

我們在創建一個Factory Class,用於創建TransparentProxy。在PIAB中,這樣一個Class由Microsoft.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();
    }
}

我們來看看程序運行後的結果:

可以看到正式我們需要的結果。從這個例子中我們可以看到,我們的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來表示)。

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 List、Invocation Context和Target Object.InvokeHandlerDelegate代表的是對CallHandler的調用,由於所有的CallHandler被串成一個CallHandler Pipeline,在調用了當前CallHandler之後,需要調用Pipeline中後一個CallHandler,對後一個CallHandler調用通過一個Delegate,GetNextHandlerDelegate來表示,而該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.handlers.Count)
    {
      ICallHandler local1 = this.handlers[handlerIndex];
      return new InvokeHandlerDelegate(local1.Invoke);
    }
    return target;
  });
}

邏輯並不復雜,按照CallHandler List的先後順序逐個調用,最後調用Target Object。方法中的第二個參數代表target代表對Target Object的調用。

四、PIAB中的Custom RealProxy:InterceptingRealProxy

我們一直再強調,PIAB實際上是通過自定義RealProxy來實現的。而且在第二節我們也實驗了這種做法的可行性。我們現在就來看看PIAB的Custom RealProxy:Microsoft.Practices.EnterpriseLibrary.PolicyInjection.RemotingInterception.InterceptingRealProxy。

public class InterceptingRealProxy : RealProxy, IRemotingTypeInfo
{
  // Fields
  private Dictionary<MethodBase, HandlerPipeline> memberHandlers;
  private readonly object target;
  private string typeName;
  // Methods 
  public InterceptingRealProxy(object target, Type classToProxy, PolicySet policies);
  private void AddHandlersForInterface(Type targetType, Type itf);
  private void AddHandlersForType(Type classToProxy, PolicySet policies);
  public bool CanCastTo(Type fromType, object o);
  public override IMessage Invoke(IMessage msg);
  // Properties
  public object Target { get; }
  public string TypeName { get; set; }
}

上面是它所有的成員定義,其中memberHandlers是一個以MethodBase為Key的Dictionary,表示應用在Target Object上面的所有的CallHandler Pipeline。通過這個很容易獲得某個具體的方法調用的Pipeline。而target值得是真正的Target Object。

我們著重來看看Invoke的定義:

public override IMessage Invoke(IMessage msg)
{
  HandlerPipeline pipeline;
  IMethodCallMessage callMessage = (IMethodCallMessage) msg;
  if (this.memberHandlers.ContainsKey(callMessage.MethodBase))
  {
    pipeline = this.memberHandlers[callMessage.MethodBase];
  }
  else
  {
    pipeline = new HandlerPipeline();
  }
  RemotingMethodInvocation invocation = new RemotingMethodInvocation(callMessage, this.target);
  return ((RemotingMethodReturn) pipeline.Invoke(invocation, delegate (IMethodInvocation input, GetNextHandlerDelegate getNext) {
    try
    {
      object returnValue = callMessage.MethodBase.Invoke(this.target, invocation.Arguments);
      return input.CreateMethodReturn(returnValue, invocation.Arguments);
    }
    catch (TargetInvocationException exception)
    {
      return input.CreateExceptionMethodReturn(exception.InnerException);
    }
  })).ToMethodReturnMessage();
}

上面的一段代碼不長,多看幾遍應該不難理解。總的來說上面的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,另一種不需要:

public static TInterface Create<TObject, TInterface>(params object[] args);
public static TObject Create<TObject>(params object[] args);

其實還有兩個重載,在這裡就不需要多做介紹了。在具體的實現中,最終又是調用一個PolicyInjector對象來實現的。

public static TObject Create<TObject>(params object[] args)
{
  return DefaultPolicyInjector.Create<TObject>(args);
}
public static TInterface Create<TObject, TInterface>(params object[] args)
{
returnDefaultPolicyInjector.Create<TObject, TInterface>(args);
}

PolicyInjector是一個Abstract Class。其中Policies屬性代表應用在該對象上的所有Policy(Policy = CallHandler + Matching Rule)

[CustomFactory(typeof(PolicyInjectorCustomFactory))]
public abstract class PolicyInjector
{
  // Fields
  private PolicySetpolicies;
  // Methods
  public PolicyInjector();
  public PolicyInjector(PolicySet policies);
  public TInterface Create<TObject, TInterface>(params object[] args);
  public TObject Create<TObject>(params object[] args);
  public objectCreate(Type typeToCreate, params object[] args);
publicobjectCreate(Type typeToCreate, Type typeToReturn, params object[] args);
… … … … …
  // Properties
  public PolicySetPolicies { get; set; }
}

在PolicyInjection Class中定義了一個叫做DefaultPolicyInjector的屬性,其定義如下:

private static PolicyInjectorDefaultPolicyInjector
{
  get
  {
    if (defaultPolicyInjector == null)
    {
      lock (singletonLock)
      {
        if (defaultPolicyInjector == null)
        {
          defaultPolicyInjector = GetInjectorFromConfig(ConfigurationSourceFactory.Create());
        }
      }
    }
    return defaultPolicyInjector;
  }
}

由於上面這個方法具體調用的Stack trace太深了,不可能很詳細地指出其具體的實現。簡單地說,該屬性會返回一個默認的PolicyInjector:RemotingPolicyInjector,並對其進行初始化。初始化的內容就包括初始化所有的Policy(這就是我們在本節開始提出的CallHandler Pipeline的初始化)。由於我們可以有兩種方式將Policy映射到目標成員:Attribute和Configuration。所有具體的做法是先通過分析Configuration找出所有通過configuration方式添加的Policy,然後通過Reflection找出所有通過使用Attribute方式添加的Policy。所以,如果你通過兩種方式將相同的Policy應用到同一個對象上,該對象上將會有兩個一樣CallHandler,個人覺得這是一個值得商榷的做法,我不太贊成這樣的行為。

我們接著看PolicyInjector接著是如何工作的:

public TInterface Create<TObject, TInterface>(params object[] args)
{
  return (TInterface) this.Create(typeof(TObject), typeof(TInterface), args);
}
public TObject Create<TObject>(params object[] args)
{
  return (TObject) this.Create(typeof(TObject), typeof(TObject), args);
}

上面連個方法由於調用到同一個Create Overload:

publicobjectCreate(Type typeToCreate, Type typeToReturn, params object[] args)
{
  PolicySet policiesFor = this.policies.GetPoliciesFor(typeToCreate);
  this.EnsureTypeIsInterceptable(typeToReturn, policiesFor);
  return this.DoCreate(typeToCreate, typeToReturn, policiesFor, args);
}

首先找出對應Type的Policy,然後判斷該類型是否支持Method Interception。RemotingPolicyInjector對該方法是這樣實現的:要麼繼承MarshalByRefObject的Class,要麼是個Interface。所以我們才有本節開始提出的兩個條件。

public override boolTypeSupportsInterception(Type t)
{
  if (!typeof(MarshalByRefObject).IsAssignableFrom(t))
  {
    return t.IsInterface;
  }
  return true;
}

上面連個方法由於調用到同一個Create Overload:

publicobjectCreate(Type typeToCreate, Type typeToReturn, params object[] args)
{
  PolicySet policiesFor = this.policies.GetPoliciesFor(typeToCreate);
  this.EnsureTypeIsInterceptable(typeToReturn, policiesFor);
  return this.DoCreate(typeToCreate, typeToReturn, policiesFor, args);
}

首先找出對應Type的Policy,然後判斷該類型是否支持Method Interception。RemotingPolicyInjector對該方法是這樣實現的:要麼繼承MarshalByRefObject的Class,要麼是個Interface。所以我們才有本節開始提出的兩個條件。

protected override object DoWrap(object instance, Type typeToReturn, PolicySet policiesForThisType)
{
  if (PolicyInjector.PolicyRequiresInterception(policiesForThisType))
  {
    InterceptingRealProxy proxy = new InterceptingRealProxy(this.UnwrapTarget(instance), typeToReturn, policiesForThisType);
    return proxy.GetTransparentProxy();
  }
  return instance;
}

和我們給出的例子差不多,創建RealPoxy,根據該RealProxy返回Transparent Proxy。

五、Policy Injection Design

最後給出整個PIAB實現的所使用的Class,基本上所有的Class都在上面的內容中介紹過了:

在本系列的第三部分,我將介紹如何創建Custom Handler,以及如何將他采用不同的方式應用到你的Application中。

Remoting架構的圖

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved