Unity(二)生命周期LifetimeManager


描述:Unity的生命周期是注冊的類型對象的生命周期,而Unity默認情況下會自動幫我們維護好這些對象的生命周期,我們也可以顯示配置對象的生命周期,Unity將按照配置自動管理。

//創建一個UnityContainer對象
            IUnityContainer container = new UnityContainer();
            IProduct milk = new Milk();
            IProduct sugar = new Sugar();
            //為 Milk 實例注冊默認實例
            container.RegisterInstance<IProduct>("Milk", milk);
            //為 Sugar 實例注冊命名實例,同RegisterType
            container.RegisterInstance<IProduct>("Sugar", sugar);
            string msg1 = container.Resolve<IProduct>("Milk").ShowInfo();
            string msg2 = container.Resolve<IProduct>("Sugar").ShowInfo();
            Response.Write(msg1 + "/" + msg2);

需要注意的是,使用RegisterInstance將已存在的實例注冊到UnityContainer中,默認情況下其實用的是ContainerControlledLifetimeManager,這個生命周期 是由UnityContainer來進行管理,UnityContainer會維護一個對象實例的強引用,當你將已存在的實例注冊到UnityContainer后,每次通過Resolve方法獲取對象都是同一對象,也就是單件實例(singleton instance)。

各個Unity的生命周期管理

1.TransientLifetimeManager

瞬態生命周期,默認情況下,在使用RegisterType進行對象關系注冊時如果沒有指定生命周期管理器則默認使用這個生命周期管理器,這個生命周期管理器就如同其名字一樣,當使用這種管理器的時候,每次通過Resolve或ResolveAll調用對象的時候都會重新創建一個新的對象。

需要注意的是,使用RegisterInstance對已存在的對象進行關系注冊的時候無法指定這個生命周期,否則會報異常。

代碼如下:

/// <summary>
        /// 配置文件注冊
        /// </summary>
        public void TransientLifetimeManagerCode2()
        {
            UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
            container.LoadConfiguration(section, "MyContainer");

            Response.Write("\r\n-------TransientLifetimeManager Begin------");
            Response.Write("\r\n第一次調用RegisterType注冊的對象HashCode:" +
                container.Resolve<IProduct>("Milk").GetHashCode());
            Response.Write("\r\n第二次調用RegisterType注冊的對象HashCode:" +
                container.Resolve<IProduct>("Milk").GetHashCode());
            Response.Write("\r\n-------TransientLifetimeManager End------");
        }
        /// <summary>
        /// 代碼注冊
        /// </summary>
        public void TransientLifetimeManagerCode1()
        {
            //以下2種注冊效果是一樣的
            container.RegisterType<IProduct, Milk>();
            container.RegisterType<IProduct, Milk>(new TransientLifetimeManager());
            Response.Write("\r\n-------TransientLifetimeManager Begin------");
            Response.Write("\r\n第一次調用RegisterType注冊的對象HashCode:" +
                container.Resolve<IProduct>("Milk").GetHashCode());
            Response.Write("\r\n第二次調用RegisterType注冊的對象HashCode:" +
                container.Resolve<IProduct>("Milk").GetHashCode());
            Response.Write("\r\n-------TransientLifetimeManager End------");
        }

配置文件:

<!--映射關系-->
      <register type="IProduct"  mapTo="Milk"  name="Milk">
        <!--transient:瞬態生命周期,每次通過Resolve或ResolveAll調用對象的時候都會重新創建一個新的對象。-->
        <lifetime type="transient" />
      </register>

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

其中<lifetime>有3個參數:

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

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

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

結果:第一次調用RegisterType注冊的對象HashCode:49575947 第二次調用RegisterType注冊的對象HashCode:25434065

 

2.ContainerControlledLifetimeManager

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

/// <summary>
        /// ContainerControlledLifetimeManager 代碼注冊
        /// </summary>
        public void ContainerControlledLifetimeManagerCode1()
        {
            IProduct milk = new Milk();
            container.RegisterInstance<IProduct>("Milk", milk);
            Response.Write("第一次HashCode:" + container.Resolve<IProduct>("Milk").GetHashCode());

            container.RegisterInstance<IProduct>("Milk", milk, new ContainerControlledLifetimeManager());
            Response.Write("第二次HashCode:" + container.Resolve<IProduct>("Milk").GetHashCode());
        }

配置文件:<lifetime type="singleton" />

結果:第一次HashCode:38580545 第二次HashCode:38580545

 

3.HierarchicalLifetimeManager
分層生命周期管理器,這個管理器類似於ContainerControlledLifetimeManager,也是由UnityContainer來管理,也就是單件實例。

不過與ContainerControlledLifetimeManager不同的是,這個生命周期管理器是分層的,因為Unity的容器時可以嵌套的,所以這個生命周期管理器就是針對這種情況,當使用了這種生命周期管理器,父容器和子容器所維護的對象的生命周期是由各自的容器來管理,代碼如下:

/// <summary>
        /// 分層生命周期管理器 代碼注冊
        /// </summary>
        public void HierarchicalLifetimeManagerCode1()
        {
            container.RegisterType<IProduct, Milk>(new HierarchicalLifetimeManager());
            //創建子容器
            var childContainer = container.CreateChildContainer();
            childContainer.RegisterType<IProduct, Milk>(new HierarchicalLifetimeManager());

            Response.Write("第一次HashCode:" + container.Resolve<IProduct>().GetHashCode());
            Response.Write("第二次HashCode:" + container.Resolve<IProduct>().GetHashCode());
            Response.Write("子容器 第一次HashCode:" + childContainer.Resolve<IProduct>().GetHashCode());
            Response.Write("子容器 第二次HashCode:" + childContainer.Resolve<IProduct>().GetHashCode());
        }

配置文件:<lifetime type="hierarchical" />

結果:第一次HashCode:44778609第二次HashCode:44778609子容器 第一次HashCode:6476987子容器 第二次HashCode:6476987

注:這邊需要提一下的就是,Unity這種分級容器的好處就在於我們可以對於有不同生命周期的對象放在不同的容器中,如果一個子容器被釋放,不會影響到其它子容器中的對象,但是如果根節點處父容器釋放后,所有的子容器都將被釋放。

 

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 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;

            txtResult.Text += "-------PerResolveLifetimeManager Begin------ <br />";

            txtResult.Text += ("使用了PerResolveLifetimeManager的對象 Begin <br />");
            txtResult.Text += ("通過Resolve方法獲取的View對象:" + view.GetHashCode() + " <br />");
            txtResult.Text += ("View對象中的Presenter對象所包含的View對象:" + realPresenter.View.GetHashCode() + " <br />");
            txtResult.Text += ("使用了PerResolveLifetimeManager的對象 End <br />");

            txtResult.Text += ("未使用PerResolveLifetimeManager的對象 Begin <br />");
            txtResult.Text += ("View對象中的Presenter對象:" + realPresenter.GetHashCode() + " <br />");
            txtResult.Text += ("通過Resolve方法獲取的View對象:" + tempPresenter.GetHashCode() + " <br />");
            txtResult.Text += ("未使用PerResolveLifetimeManager的對象 End <br />");
            txtResult.Text += ("-------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>

  結果:

-------PerResolveLifetimeManager Begin------ 
使用了PerResolveLifetimeManager的對象 Begin 
通過Resolve方法獲取的View對象:60062986 
View對象中的Presenter對象所包含的View對象:60062986 
使用了PerResolveLifetimeManager的對象 End 
未使用PerResolveLifetimeManager的對象 Begin 
View對象中的Presenter對象:58180283 
通過Resolve方法獲取的View對象:14497132 
未使用PerResolveLifetimeManager的對象 End 
-------PerResolveLifetimeManager Begin------

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

 

5.PerThreadLifetimeManager

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

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

結果:

-------PerResolveLifetimeManager Begin------ 
默認線程 Begin 
第一調用:33863240 
第二調用:33863240 
默認線程 End 

新建線程 Begin 
第一調用:20518217 
第二調用:20518217 
新建線程 End 
-------PerResolveLifetimeManager End------

注:一般來說不建議在使用RegisterInstance對已存在的對象注冊關系時使用PerThreadLifetimeManager,因為此時的對象已經在一個線程內創建了,如果再使用這個生命周期管理器,將無法保證其正確調用。

 

6.ExternallyControlledLifetimeManager

外部控制生命周期管理器,這個生命周期管理允許你使用RegisterType和RegisterInstance來注冊對象之間的關系,但是其只會對對象保留一個弱引用,其生命周期交由外部控制,也就是意味着你可以將這個對象緩存或者銷毀而不用在意UnityContainer,而當其他地方沒有強引用這個對象時,其會被GC給銷毀 掉。

在默認情況下,使用這個生命周期管理器,每次調用Resolve都會返回同一對象(單件實例),如果被GC回收后再次調用Resolve方法將會重新創建新的對象,示例代碼如下:

public void ExternallyControlledLifetimeManagerCode()
        {
            container.RegisterType<IProduct, Milk>(new ExternallyControlledLifetimeManager());
            var myClass1 = container.Resolve<IProduct>();
            var myClass2 = container.Resolve<IProduct>();
            txtResult.Text += ("-------ExternallyControlledLifetimeManager Begin------ <br />");
            txtResult.Text += ("第一次調用:" + myClass1.GetHashCode() + " <br />");
            txtResult.Text += ("第二次調用:" + myClass2.GetHashCode() + " <br />");

            myClass1 = myClass2 = null;
            GC.Collect();

            txtResult.Text += ("****GC回收過后**** <br />");
            txtResult.Text += ("第一次調用:" + container.Resolve<IProduct>().GetHashCode() + " <br />");
            txtResult.Text += ("第二次調用:" + container.Resolve<IProduct>().GetHashCode() + " <br />");
            txtResult.Text += ("-------ExternallyControlledLifetimeManager End------");
        }

結果:

-------ExternallyControlledLifetimeManager Begin------ 
第一次調用:51935804 
第二次調用:51935804 
****GC回收過后**** 
第一次調用:22470659 
第二次調用:22470659 
-------ExternallyControlledLifetimeManager End------ 

來源:http://www.cnblogs.com/qqlin/archive/2012/10/17/2720829.html

原文鏈接: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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM