程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 淺析如何在ObjectBuilder2中用動態方法進行構造器注入

淺析如何在ObjectBuilder2中用動態方法進行構造器注入

編輯:關於.NET

一、前言

在我看來,OB2是一個用來構建和管理對象的開放性的框架,我們可以根據自己的需求去 擴展它,例如擴展它可以實現依賴注入(如MS的Unity)。我認為OB2最大的亮點之一是在提供 了對象創建框架的同時能夠管理對象以及對象之間的依賴關系,控制對象構建和銷毀過程, 這樣對象的創建就不是直接去使用new而對象的銷毀也不僅僅只靠GC了。要說OB2所使用的設 計模式,我覺得可以認為是大量使用策略(Strategy)模式並輔以責任鏈模式,通過責任鏈組 織對象創建或銷毀的次序及步驟。

二、關鍵組成部分

1、Locator

利用一個Dictionary對創建的對象建立<鍵,值>對,之後通過key可以找到相應的 對象,需要注意的是,這裡的值是通過System.WeakReference包裝過的短弱引用對象,之所 以這樣做是想在出現內存壓力或其他情況時允許Locator中的對象被GC回收,而強調使用短弱 引用是保證出現內存壓力時,對象最晚會在進行第一次finalize後被回收,注意GC和 finalize使用兩個不同的工作線程,GC線程被調用會觸發finalize線程開始工作,但GC線程 完成收集工作並不意味著finalize線程工作結束,這就意味著對象在GC線程開始工作到 finalize線程釋放目標對象之前,目標對象都可以被激活,而這裡不用強弱引用恐怕是因為 強弱引用對象會在第一次finalize和第二次finalize之間仍然存在,如果這時候目標對象被 激活可能導致對象狀態錯誤(因為也許對象中某些部分已經被回收)。

2、LifetimeContainer

以一個只讀的List<Object>結構存儲對象,管理對象的生命周期。不管是值類型還 是引用類型,都可以被加入到容器中,不管是同一個類型的同一個對象還是同一個類型的不 同對象都可以被同時加入容器,可以對容器中所有實現IDisposable接口的對象進行統一的釋 放。

3、Strategy與Policy

Strategy,是對算法的封裝,OB2中大量使用了策略模式,我們可以將對象的創建、銷毀 過程抽象成算法,利用Strategy進行封裝從而實現復用和倒置通用算法與具體實現之間的依 賴關系。

Policy,通過傳遞某些信息輔助Strategy,所謂傳遞的信息是多種多樣的,只要Strategy 需要就可以通過Policy傳入,因此有相當大的靈活性,當然Strategy也可以不使用Policy, 這就意味著該Strategy不需要通過外部傳遞給它信息就能實現算法。

Strategy與Policy相輔相成,為我們創建對象提供了很大的靈活度。

4、BuilderContext

構建上下文,我們的Strategy是以流水線的方式執行的,那麼對上下文環境進行封裝,為 其提供一個統一的狀態存儲和共享的環境是有必要的,這樣每個Strategy所針對的上下文環 境是確定的,可以清晰的看到這些變化,也就可以隨時截取這些變化,我們總是習慣將變化 的東西封裝起來,當然不同的上下文之間又可以起到相互隔離的作用,避免有意或無意的干 擾,讓對象只知道它應該知道的信息。最後,在構建上下文中存儲了最終生成的對象。

三、利用動態方法進行構造器注入

1、基本思路

確定待構建類型TypeToBuild;

為TypeToBuild建立一個動態方法(Dynamic Method),該動態方法的方法體的構建利用反 射發出(Reflection Emit)實現(使用MSIL),大致過程是:找到TypeToBuild的構造函數(優先 查找打了構造器注入標志的構造函數),利用特定Strategy+Policy對其參數進行解析並將解 析結果壓入Evaluation Stack,最後調用相應指令生成對象;

大家可以看到,這裡出現了一個新的變化點,所以需要建立一個與構造器注入相關的上下 文環境,這個環境封裝如下內容:待構建類型TypeToBuild、為生成TypeToBuild所需要的動 態方法(System. DynamicMethod)、用來在內存中生成MSIL代碼的ILGenerator對象、局部變 量,如圖:

由於構建過程中在不同策略間負責傳遞信息的是IBuilderContext(構建上下文),因此, 自然而然的在構建我們的動態方法時需要的信息也是從IBuilderContext中獲取,所以我們要 構建的用於生成TypeToBuild類型對象的方法原型類似於這樣:

Void BuildUp_*** (IBuilderContext context)

最終,生成的BuildUp_***方法被調用後即可生成相應對 象。

2、相關Strategy和Policy分析

Strategy:

① BuildPlanStrategy,用於構建目標類型的對象

它會在當前上下文中查找是否有構建 對象的計劃(繼承了IBuildPlanPolicy接口的Policy),如果沒有則在當前上下文中獲取繼承 了IBuildPlanCreatorPolicy類型的Policy來生成一個構建計劃,最後調用構建計劃的 BuildUp方法生成對象。如圖所示:

② DynamicMethodConstructorStrategy,用於構建與構造器注入相關的上下文環境,包括動態 方法的方法體,這裡給出構建動態方法的MSIL偽碼。 

MSIL偽碼

.method public hidebysig virtual instance void  BuildUp_"methodname" (IBuilderContext context) cil managed
{
     .maxstack 3
    .locals init(
            [0]  class typeToBuild 'existingObjectLocal'
            [1] class  [mscorlib]System.String 'currentParameterName')
    L_0000:  ldarg.0
    L_0001: callvirt 'Existing'
    L_0002: cast  class typeToBuild
    L_0003: stloc.0 //完成獲取方法所在類型的Type操 作並存在局部變量existingObjectLocal中,
                     //Evaluation Stack為空
     
    L_0004: ldloc.0  //將 要構建的Type加載到局部變量區中
    L_001f: ldnull
    L_0005:  ceq
    L_0006: brfalse existingObjectNotNull  //待構建類型為null 則返回
    L_0007: ldarg.0
    L_0008: call  'ThrowForAttemptingToConstructInterface' //判斷當前類型是否為接口
                                                            //是,則拋出異常
    L_0009:  BeginExceptionBlock
    L_002e: nop
    L_002f: ldstr  'parameters[i].Name' //獲取當前參數名稱並存儲於局部變量
                                         //currentParameterName中
    L_0010: stloc.1
    L_0012:  ldarg.0  //將上下文實例壓入Evaluation Stack
    L_0013: callvirt  'Policies' //從上下文獲取PolicyList
    L_0014: ldtoken 'typeof  (IDependencyResolverPolicy)'//從MetaData
                                                         // 獲取IDependencyResolverPolicy
                                                         //類型的 token
    L_0015: call System.Type.'GetTypeFromHandle' //執行call指令 ,依據句柄獲取類型,
    //返回值為類型,為什麼要用這種方式?因為Policy 在存的時候以<IDependencyResolverPolicy,Key>
    //方式存儲,所以 需要去metaData中找到IDependencyResolverPolicy的Type
    L_0016: ldstr  'key with GUID' //獲取當前參數的BuildKey用來取出相應的Policy
     L_0017: callvirt 'IPolicyList.Get' //調用其Get方法取出相應Policy
     L_0018: cast class 'typeof(IDependencyResolverPolicy)'//完成Policy向
                                      //IDependencyResolverPolicy的類型轉換
    L_0019: ldarg.0 //向 Evaluation Stack中壓入上下文
    L_0020: callvirt 'Resolve' //調用找 出的DependencyResolverPolicy的Resolve方法
                                //以生成參數並將參數壓入Evaluation Stack
     L_0021: brtrue L_002e //構造函數的所有參數都解析完畢則跳出

     L_0022: ldnull
    L_0023: stloc.1 //清空局部變量 currentParameterName先
    L_0024: newobj 'selectedCtor.Constructor'  //其實就是將Evaluation Stack中參數先彈出,
                                               //然後依據 ConstructorInfo信息調用待構建
                                     //類型的構造函數,並將生成的對象壓入Evaluation  Stack
                                               
     L_0025: stloc.0 //將生成的對象保存到局部 變量區中,保證Evaluation Stack為空
    L_0026: BeginCatchBlock //傳入 類型為Exception
    exceptionOccuredInResolution:
    L_0027:  ldloc.1
    L_0028: ldnull
    L_0029: ceq
     L_0030: brfalse exceptionOccuredInResolution
    L_0031: rethrow      //處理完異常之後再將其拋出
    L_0032: ldloc.1
     L_0033: ldstr 'signatureString' 
    L_0034: ldarg.0
     L_0035: call 'throwForResolutionFailed'
    L_0036:  EndExceptionBlock
    existingObjectNotNull:
}

構建流 程類似如下:

Policy:

① DefaultDynamicBuilderMethodCreatorPolicy,用來生成動態方法原 型:

Void BuildUp_*** (IBuilderContext context)

② ConstructorSelectorPolicy,輔助DynamicMethodConstructorStrategy,用於查找類型中打 了構造器注入標記的構造函數,並將其參數進行解析,對不同的參數如何解析是需要我們自 己擴展的。

③DynamicMethodBuildPlanCreatorPolicy,用來生成構建計劃,利用 DefaultDynamicBuilderMethodCreatorPolicy和DynamicMethodConstructorStrategy構建 BuildUp_***的動態方法並將結果保存於動態方法的上下文環境:

DynamicBuildPlanGenerationContext,最後利用動態方法的上下文環境生成該方法 的委托並作為參數生成構建計劃。

3、實例分析

需求:模擬建立一個線程池 ThreadPool,建立對象時需要傳入線程池的名字和允許最大線程數,使用OB2實現對象構建。

1、ThreadPool的原型如下,其構造函數有兩個參數,分別為String類型和Int32類型 :

ThreadPool

public class ThreadPool
    {
         private String poolName = String.Empty;
         private Int32 maxThread = 10;
        //[InjectionThreadPoll]
        public ThreadPool()
        {
             Console.ForegroundColor = ConsoleColor.Red;
             Console.WriteLine("The default constructor is called...");
         }
        [InjectionThreadPoll]
        public  ThreadPool(String name, Int32 num)
        {
             this.poolName = name;
            this.maxThread  = num;
            OutPut();
        }
         private void OutPut()
        {
             this.SetForegroundColor(ConsoleColor.White);
             Console.Write("The thread pool's name is ");
             this.SetForegroundColor(ConsoleColor.Red);
             Console.WriteLine(this.poolName);
             this.SetForegroundColor(ConsoleColor.White);
             Console.Write("The number of threads in a thread pool should not exceed  ");
            this.SetForegroundColor(ConsoleColor.Red);
            Console.WriteLine(this.maxThread);
         }
        private void SetForegroundColor(ConsoleColor color)
        {
            Console.ForegroundColor =  color;
     }

2、需要使用的Strategy和Policy如下:

① OB2自定義的

BuildPlanStrategy、DynamicMethodConstructorStrategy、 DefaultDynamicBuilderMethodCreatorPolicy、ConstructorSelectorPolicy、 DynamicMethodBuildPlanCreatorPolicy

② 需要自己擴展的

這部分主要集中到參數的解析上,我的做法是建立一個ParametersResolveStrategy,專 門用來解析參數,此時傳來的上下文中包含參數的類型,利用參數類型去尋找相應的Policy ,ParametersResolveStrategy+Policy可以得到參數實例。

參數通過配置文件讀出,Policy的實現如下:

ParameterChooserPolicy

public interface IParameterChooserPolicy : IBuilderPolicy
     {
         Object GetValue();
     }
     public class ParameterChooserPolicy<TParameterType> :  IParameterChooserPolicy
     {
         public virtual Object GetValue()
         {
             Object obj = ConfigurationManager.AppSettings [GetNameOfType(typeof(TParameterType))];
             return Convert.ChangeType(obj,typeof (TParameterType));
         }
         private String GetNameOfType(Type type)
         {
             return type.ToString();
         }
}

3、建立一個ThreadPoolBuilder類,用來配置Strategy和Policy,實現如下:

ThreadPoolBuilder

public class ThreadPoolBuilder<TTypeToBuild>
         where TTypeToBuild : class
     {
         private NamedTypeBuildKey key;
         private StagedStrategyChain<BuilderStage> strategies  = new StagedStrategyChain<BuilderStage>();
         private IReadWriteLocator locator = null;
         private ILifetimeContainer lifetime = null;
         public ThreadPoolBuilder()
         {
             key = new NamedTypeBuildKey(typeof (TTypeToBuild));
             this.strategies.Add(new BuildPlanStrategy(),  BuilderStage.PreCreation);
             this.strategies.Add(new ParametersResolveStrategy(),  BuilderStage.Creation);
             this.SetInnerStrategies();
         }
         public IReadWriteLocator Locator
         {
             get
             {
                 return this.locator;
             }
             set
             {
                 this.locator = value;
             }
         }
         public ILifetimeContainer Liftime
         {
             get
             {
                 return this.lifetime;
             }
             set
             {
                 this.lifetime = value;
             }
         }
         public virtual IStagedStrategyChain SetInnerStrategies()
         {
             StagedStrategyChain<BuilderStage>  innerStrategies = new StagedStrategyChain<BuilderStage>();
             innerStrategies.Add(new  DynamicMethodConstructorStrategy(), BuilderStage.PreCreation);
             return innerStrategies;
         }
         public virtual IPolicyList GetPolicies()
         {
             PolicyList policies = new PolicyList();
             policies.Set<IBuildPlanCreatorPolicy>(new  DynamicMethodBuildPlanCreatorPolicy(SetInnerStrategies()), key);
             policies.Set<IDynamicBuilderMethodCreatorPolicy>(new  DefaultDynamicBuilderMethodCreatorPolicy(), key);
             policies.Set<IConstructorSelectorPolicy>(new  ConstructorSelectorPolicy<InjectionThreadPollAttribute>(), key);
             policies.Set<IParameterChooserPolicy>(new  ParameterChooserPolicy<String>(), typeof(String));
             policies.Set<IParameterChooserPolicy>(new  ParameterChooserPolicy<Int32>(), typeof(Int32));
             return policies;
         }
         public TTypeToBuild BuildUp()
         {
             return new Builder().BuildUp<TTypeToBuild> (
                 this.locator,
                 this.lifetime,
                 this.GetPolicies(),
                 this.strategies.MakeStrategyChain(),
                 key,
                 null);
         }
}

最後,使用的時候這樣就可以了:

ThreadPool t1 = new ThreadPoolBuilder<ThreadPool>().BuildUp();

4、總結

我覺得學習OB2,看懂每行代碼不如看懂它的思想,理解為什麼這麼設計才是關鍵。順便 說一句,據說OB2中動態方法這一部分是為了Unity而加的,在OB2中用動態方法分別實現了構 造器注入、屬性注入和方法注入,實現類似,基本上看懂一個就看懂全部了,希望大家多提 寶貴意見,看看怎麼樣才能把OB2用好。

本文源代碼: http://files.cnblogs.com/vivounicorn/Ob2DynamicMethodSolution.rar

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