[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,里面的文章写的蛮好的,你们能够查考。

相关文章
相关标签/搜索