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("
-------TransientLifetimeManager Begin------");
            Response.Write("
第一次调用RegisterType注册的对象HashCode:" +
                container.Resolve<IProduct>("Milk").GetHashCode());
            Response.Write("
第二次调用RegisterType注册的对象HashCode:" +
                container.Resolve<IProduct>("Milk").GetHashCode());
            Response.Write("
-------TransientLifetimeManager End------");
        }
        /// <summary>
        /// 代码注册
        /// </summary>
        public void TransientLifetimeManagerCode1()
        {
            //以下2种注册效果是一样的
            container.RegisterType<IProduct, Milk>();
            container.RegisterType<IProduct, Milk>(new TransientLifetimeManager());
            Response.Write("
-------TransientLifetimeManager Begin------");
            Response.Write("
第一次调用RegisterType注册的对象HashCode:" +
                container.Resolve<IProduct>("Milk").GetHashCode());
            Response.Write("
第二次调用RegisterType注册的对象HashCode:" +
                container.Resolve<IProduct>("Milk").GetHashCode());
            Response.Write("
-------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

原文地址:https://www.cnblogs.com/xsj1989/p/5784330.html