[IoC容器Unity]第二回:Lifetime Managers生命周期
1.引言
Unity的生命周期是注册的类型对象的生命周期,而Unity默认情况下会自动帮我们维护好这些对象的生命周期,我们也可以显示配置对象的生命周期,Unity将按照配置自动管理,非常方便,下面就介绍一下 Unity中内置的生命周期管理器。
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),具体有关生命周期相关信息在下面进行介绍。
由于RegisterInstance是对已存在的实例进行注册,所以无法通过配置文件来进行配置。有关RegisterInstance方法的其他重载我这边就不详细介绍了,可以点此查看详细的重载方法介绍。
下面将详细介绍各个Unity的生命周期管理
2.1 TransientLifetimeManager
瞬态生命周期,默认情况下,在使用RegisterType进行对象关系注册时如果没有指定生命周期管理器则默认使用这个生命周期管理器,这个生命周期管理器就如同其名字一样,当使用这种管理器的时候,每次通过Resolve或ResolveAll调用对象的时候都会重新创建一个新的对象。
需要注意的是,使用RegisterInstance对已存在的对象进行关系注册的时候无法指定这个生命周期,否则会报异常。
代码如下:
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------"); }
效果图如下,可以看出每次产生的对象都是不同的:
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>
效果图如下,可以看出每次获取的对象都是同一对象:
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>
具体的效果图如下,可以看出父级和子级维护不同对象实例:
这边需要提一下的就是,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>
读取配置文件代码类似于前面其他配置文件读取代码,这里就不展示,具体请看示例代码。
具体的效果图如下:
可以看出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------"); }
有关配置相关的代码与前面的生命周期管理器差不多,这边就不贴代码了,请看示例代码。
具体效果图如下:
同时需要注意的是,一般来说不建议在使用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------"); }
有关配置相关的代码与前面的生命周期管理器差不多,这边就不贴代码了,请看示例代码。
效果图如下:
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,里面的文章写的蛮好的,大家可以查考。
出处:http://qixuejia.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。