[IoC容器Unity]第二回:Lifetime Managers生命週期

1.引言

Unity的生命週期是註冊的類型對象的生命週期,而Unity默認狀況下會自動幫咱們維護好這些對象的生命週期,咱們也能夠顯示配置對象的生命週期,Unity將按照配置自動管理,很是方便,下面就介紹一下 Unity中內置的生命週期管理器。html

2.Lifetime Managers生命週期管理

準備如下類關係,用於演示
緩存

有2個接口類:IClass(班級接口)和ISubject(科目接口),其分別有2個實現類。看下面一個示例
函數

public static void RegisterInstance()
{
    IClass myClass = new MyClass();
    IClass yourClass = new YourClass();
    //爲myClass實例註冊默認實例
    container.RegisterInstance<IClass>(myClass);
    //爲yourClass實例註冊命名實例,同RegisterType
    container.RegisterInstance<IClass>("yourInstance", yourClass);
 
    container.Resolve<IClass>().ShowInfo(); 
    container.Resolve<IClass>("yourInstance").ShowInfo();
}

這段代碼很簡單,就是經過RegisterInstance方法爲已存在的對象進行註冊,這樣能夠經過UnityContainer來管理這些對象實例的生命週期。學習

須要注意的是,使用RegisterInstance來將已存在的 實例註冊到UnityContainer中,默認狀況下其實用的是ContainerControlledLifetimeManager,這個生命週期 是由UnityContainer來進行管理,UnityContainer會維護一個對象實例的強引用,當你將已存在的實例註冊到 UnityContainer後,每次經過Resolve方法獲取對象都是同一對象,也就是單件實例(singleton instance),具體有關生命週期相關信息在下面進行介紹。spa

因爲RegisterInstance是對已存在的實例進行註冊,因此沒法經過配置文件來進行配置。有關RegisterInstance方法的其餘重載我這邊就不詳細介紹了,能夠點此查看詳細的重載方法介紹。線程

下面將詳細介紹各個Unity的生命週期管理3d

2.1 TransientLifetimeManager

瞬態生命週期,默認狀況下,在使用RegisterType進行對象關係註冊時若是沒有指定生命週期管理器則默認使用這個生命週期管理器,這個生命週期管理器就如同其名字同樣,當使用這種管理器的時候,每次經過ResolveResolveAll調用對象的時候都會從新建立一個新的對象。code

須要注意的是,使用RegisterInstance對已存在的對象進行關係註冊的時候沒法指定這個生命週期,不然會報異常。htm

代碼以下:對象

public static void TransientLifetimeManagerCode()
{
    //如下2種註冊效果是同樣的
    container.RegisterType<IClass, MyClass>();
    container.RegisterType<IClass, MyClass>(new TransientLifetimeManager());
    Console.WriteLine("-------TransientLifetimeManager Begin------");
    Console.WriteLine("第一次調用RegisterType註冊的對象HashCode:" +
        container.Resolve<IClass>().GetHashCode());
    Console.WriteLine("第二次調用RegisterType註冊的對象HashCode:" +
        container.Resolve<IClass>().GetHashCode());
    Console.WriteLine("-------TransientLifetimeManager End------");
}

配置文件以下:

<register type="IClass" mapTo="MyClass">
  <lifetime type="transient" />
  <!--<lifetime type="SessionLifetimeManager"
      value="Session#1" typeConverter="SessionLifetimeConverter" />-->
</register>

若是想在配置文件中在在註冊關係的時候更改一個生命週期管理器只需在<register>配置節下新增<lifetime>既可(若是不新增則默認使用TransientLifetimeManager)。

其中<lifetime>有3個參數:

1)type,生命期週期管理器的類型,這邊能夠選擇Unity內置的,也可使用自定義的,其中內置的生命週期管理器會有智能提示。

2)typeConverter,生命週期管理器轉換類,用戶自定義一個生命週期管理器的時候所建立一個轉換器。

3)value,初始化生命週期管理器的值。

配置文件讀取代碼:

public static void TransientLifetimeManagerConfiguration()
{
    //獲取指定名稱的配置節
    UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
    container.LoadConfiguration(section, "First");
 
    Console.WriteLine("-------TransientLifetimeManager Begin------");
    Console.WriteLine("第一次調用RegisterType註冊的對象HashCode:" +
        container.Resolve<IClass>("transient").GetHashCode());
    Console.WriteLine("第二次調用RegisterType註冊的對象HashCode:" +
        container.Resolve<IClass>("transient").GetHashCode());
    Console.WriteLine("-------TransientLifetimeManager End------");
}

效果圖以下,能夠看出每次產生的對象都是不一樣的:

pic72

 

2.2 ContainerControlledLifetimeManager

容器控制生命週期管理,這個生命週期管理器是RegisterInstance默認使用的生命週期管理器,也就是單件實例,UnityContainer會維護一個對象實例的強引用,每次調用的時候都會返回同一對象,示例代碼以下:

 

public static void ContainerControlledLifetimeManagerCode()
{
    IClass myClass = new MyClass();
    //如下2種註冊效果是同樣的
    container.RegisterInstance<IClass>("ccl", myClass);
    container.RegisterInstance<IClass>("ccl", myClass, new ContainerControlledLifetimeManager());
 
    container.RegisterType<IClass, MyClass>(new ContainerControlledLifetimeManager());
    Console.WriteLine("-------ContainerControlledLifetimeManager Begin------");
    Console.WriteLine("第一次調用RegisterType註冊的對象HashCode:" +
        container.Resolve<IClass>().GetHashCode());
    Console.WriteLine("第二次調用RegisterType註冊的對象HashCode:" +
        container.Resolve<IClass>().GetHashCode());
    Console.WriteLine("第一次調用RegisterInstance註冊的對象HashCode:" +
        container.Resolve<IClass>("ccl").GetHashCode());
    Console.WriteLine("第二次調用RegisterInstance註冊的對象HashCode:" +
        container.Resolve<IClass>("ccl").GetHashCode());
    Console.WriteLine("-------ContainerControlledLifetimeManager End------");
}

配置文件以下:

<register type="IClass" mapTo="MyClass" name="ccl">
  <lifetime type="singleton" />
</register>

效果圖以下,能夠看出每次獲取的對象都是同一對象:

pic73

2.3 HierarchicalLifetimeManager

分層生命週期管理器,這個管理器相似於ContainerControlledLifetimeManager,也是由UnityContainer來管理,也就是單件實例。不過與ContainerControlledLifetimeManager不 同的是,這個生命週期管理器是分層的,由於Unity的容器時能夠嵌套的,因此這個生命週期管理器就是針對這種狀況,當使用了這種生命週期管理器,父容器 和子容器所維護的對象的生命週期是由各自的容器來管理,代碼以下(RegisterInstance狀況也相似,這邊就不展現了)

public static void HierarchicalLifetimeManagerCode()
{
    container.RegisterType<IClass, MyClass>(new HierarchicalLifetimeManager());
    //建立子容器
    var childContainer = container.CreateChildContainer();
    childContainer.RegisterType<IClass, MyClass>(new HierarchicalLifetimeManager());
 
    Console.WriteLine("-------ContainerControlledLifetimeManager Begin------");
    Console.WriteLine("第一次調用父容器註冊的對象HashCode:" +
        container.Resolve<IClass>().GetHashCode());
    Console.WriteLine("第二次調用父容器註冊的對象HashCode:" +
        container.Resolve<IClass>().GetHashCode());
    Console.WriteLine("第一次調用子容器註冊的對象HashCode:" +
        childContainer.Resolve<IClass>().GetHashCode());
    Console.WriteLine("第二次調用子容器註冊的對象HashCode:" +
        childContainer.Resolve<IClass>().GetHashCode());
    Console.WriteLine("-------ContainerControlledLifetimeManager End------");
}

因爲配置文件不能配置這種層級效果,因此配置這種生命週期時只須要更改下生命週期名稱:

<register type="IClass" mapTo="MyClass" name="hl">
  <lifetime type="hierarchical" />
</register>

具體的效果圖以下,能夠看出父級和子級維護不一樣對象實例:

pic74

這邊須要提一下的就是,Unity這種分級容器的好處就在於咱們能夠對於有不一樣生命週期的對象放在不一樣的容器中,若是一個子容器被釋放,不會影響到其它子容器中的對象,可是若是根節點處父容器釋放後,全部的子容器都將被釋放。

2.4 PerResolveLifetimeManager

這個生命週期是爲了解決循環引用而重複引用的生命週期,先看一下微軟官方給出的實例:

public interface IPresenter
{ }
 
public class MockPresenter : IPresenter
{
    public IView View { get; set; }
 
    public MockPresenter(IView view)
    {
        View = view;
    }
}
 
public interface IView
{
    IPresenter Presenter { get; set; }
}
 
public class View : IView
{
    [Dependency]
    public IPresenter Presenter { get; set; }
}

從這個例子中能夠看出,有2個接口IPresenter和IView,還有2個類MockPresenter和View分別實現這2個接口,同時這2個類 中都包含了對另一個類的對象屬性,這個就是一個循環引用,而對應的這個生命週期管理就是針對這種狀況而新增的,其相似於 TransientLifetimeManager,可是其不一樣在於,若是應用了這種生命週期管理器,則在第一調用的時候會建立一個新的對象,而再次經過 循環引用訪問到的時候就會返回先前建立的對象實例(單件實例),代碼以下:

public static void PerResolveLifetimeManagerCode()
{
    var container = new UnityContainer()
    .RegisterType<IPresenter, MockPresenter>()
    .RegisterType<IView, View>(new PerResolveLifetimeManager());
 
    var view = container.Resolve<IView>();
    var tempPresenter = container.Resolve<IPresenter>();
    var realPresenter = (MockPresenter)view.Presenter;
 
    Console.WriteLine("-------PerResolveLifetimeManager Begin------");
    Console.WriteLine("使用了PerResolveLifetimeManager的對象 Begin");
    Console.WriteLine("經過Resolve方法獲取的View對象:" +
        view.GetHashCode());
    Console.WriteLine("View對象中的Presenter對象所包含的View對象:" +
        realPresenter.View.GetHashCode());
    Console.WriteLine("使用了PerResolveLifetimeManager的對象 End");
    Console.WriteLine("");
    Console.WriteLine("未使用PerResolveLifetimeManager的對象 Begin");
    Console.WriteLine("View對象中的Presenter對象:" +
        realPresenter.GetHashCode());
    Console.WriteLine("經過Resolve方法獲取的View對象:" +
        tempPresenter.GetHashCode());
    Console.WriteLine("未使用PerResolveLifetimeManager的對象 End");
    Console.WriteLine("-------PerResolveLifetimeManager Begin------");
}

從代碼中能夠看出,在註冊對象的時候,僅對IView和View應用了PerResolveLifetimeManager,因此第二次訪問View對象會返回同一實例。

具體配置文件以下,有關構造函數注入和屬性注入的內容在下一篇文章中進行介紹

<alias alias="IPresenter" type="UnityStudyConsole.IPresenter, UnityStudyConsole" />
<alias alias="IView" type="UnityStudyConsole.IView, UnityStudyConsole" />
<alias alias="MockPresenter" type="UnityStudyConsole.MockPresenter, UnityStudyConsole" />
<alias alias="View" type="UnityStudyConsole.View, UnityStudyConsole" />
<container name="Second">
  <register type="IPresenter" mapTo="MockPresenter">
    <constructor>
      <param name ="view" type="IView">
      </param>
    </constructor>
  </register>
  <register type="IView" mapTo="View" >
    <lifetime type="perresolve"/>
    <property name="Presenter" dependencyType="IPresenter"></property>
  </register>
</container>

讀取配置文件代碼相似於前面其餘配置文件讀取代碼,這裏就不展現,具體請看示例代碼。

具體的效果圖以下:

pic75

能夠看出2次調用View對象的HashCode都是同樣的,而Presenter對象的HashCode不一樣。

2.5 PerThreadLifetimeManager

每線程生命週期管理器,就是保證每一個線程返回同一實例,具體代碼以下:

public static void PerThreadLifetimeManagerCode()
{
    container.RegisterType<IClass, MyClass>(new PerThreadLifetimeManager());
    var thread = new Thread(new ParameterizedThreadStart(Thread1));
    Console.WriteLine("-------PerResolveLifetimeManager Begin------");
    Console.WriteLine("默認線程 Begin");
    Console.WriteLine("第一調用:" +
        container.Resolve<IClass>().GetHashCode());
    Console.WriteLine("第二調用:" +
        container.Resolve<IClass>().GetHashCode());
    Console.WriteLine("默認線程 End");
    thread.Start(container);
} 
public static void Thread1(object obj)
{
    var tmpContainer = obj as UnityContainer;
    Console.WriteLine("新建線程 Begin");
    Console.WriteLine("第一調用:" +
        tmpContainer.Resolve<IClass>().GetHashCode());
    Console.WriteLine("第二調用:" +
        tmpContainer.Resolve<IClass>().GetHashCode());
    Console.WriteLine("新建線程 End");

Console.WriteLine("-------PerResolveLifetimeManager End------"); 
}

有關配置相關的代碼與前面的生命週期管理器差很少,這邊就不貼代碼了,請看示例代碼。

具體效果圖以下:

pic76

同時須要注意的是,通常來講不建議在使用RegisterInstance對已存在的對象註冊關係時使用PerThreadLifetimeManager,由於此時的對象已經在一個線程內建立了,若是再使用這個生命週期管理器,將沒法保證其正確調用。

 

2.6 ExternallyControlledLifetimeManager

外部控制生命週期管理器,這個 生命週期管理容許你使用RegisterType和RegisterInstance來註冊對象之間的關係,可是其只會對對象保留一個弱引用,其生命週期 交由外部控制,也就是意味着你能夠將這個對象緩存或者銷燬而不用在乎UnityContainer,而當其餘地方沒有強引用這個對象時,其會被GC給銷燬 掉。

在默認狀況下,使用這個生命週期管理器,每次調用Resolve都會返回同一對象(單件實例),若是被GC回收後再次調用Resolve方法將會從新建立新的對象,示例代碼以下:

public static void ExternallyControlledLifetimeManagerCode()
{
    container.RegisterType<IClass, MyClass>(new ExternallyControlledLifetimeManager());
    var myClass1 = container.Resolve<IClass>();
    var myClass2 = container.Resolve<IClass>();
    Console.WriteLine("-------ExternallyControlledLifetimeManager Begin------");
    Console.WriteLine("第一次調用:" +
        myClass1.GetHashCode());
    Console.WriteLine("第二次調用:" +
        myClass2.GetHashCode());
    myClass1 = myClass2 = null;
    GC.Collect();
    Console.WriteLine("****GC回收事後****");
    Console.WriteLine("第一次調用:" +
       container.Resolve<IClass>().GetHashCode());
    Console.WriteLine("第二次調用:" +
       container.Resolve<IClass>().GetHashCode());
 
    Console.WriteLine("-------ExternallyControlledLifetimeManager End------");
}

有關配置相關的代碼與前面的生命週期管理器差很少,這邊就不貼代碼了,請看示例代碼。

效果圖以下:

pic77

 

3.小結

因爲本身在學習Unity,在查找相關資料,在園子裏找到這篇,看了一下仍是很不錯的,因而轉載了,原文連接:http://www.cnblogs.com/kyo-yo/archive/2010/11/10/Learning-EntLib-Tenth-Decoupling-Your-System-Using-The-Unity-PART2-Learn-To-Use-Unity-Two.html,裏面的文章寫的蠻好的,你們能夠查考。

相關文章
相關標籤/搜索