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
Unity(二)生命周期LifetimeManager的更多相关文章
- Unity脚本生命周期
前言 说到生命周期,影响最深刻的是,在接触Java的JSF组件时,JSF组件的五大生命周期,全要默写出来,嘿嘿…… 总结这两天在写小怪和掉落的糖葫芦时,老是遇到GameObject未销毁,一直存在场景 ...
- Spring之bean二生命周期
上一博客主要学习了下bean的配置.注入.自定义属性编辑器,今天来熟悉bean的生命周期.在开发中生命周期是一个很常见的名词,基本每种编程语言都能找到与它关联的.关于bean的生命周期我在网上也找了好 ...
- Unity脚本生命周期与执行顺序
文章目录 脚本生命周期 MonoBehavior生命周期图 脚本执行顺序 自定义执行顺序 在Unity中,脚本可以理解为附加在游戏对象上的用于定义游戏对象行为的指令代码.必须绑定在游戏对象上才能开始它 ...
- react基础学习 二——生命周期
生命周期mount: mounting装载创建 update更新 unmounting卸载 错误捕获 注意点:生命周期函数的 作用,什么之后用 只有类式组件有生命周期,函数式组件没有生命周期 moun ...
- react学习二 生命周期
转自:https://www.cnblogs.com/gdsblog/p/7348375.html react 中compent getDefaultProps object getDefaultPr ...
- VUE 学习笔记 二 生命周期
1.除了数据属性,Vue 实例还暴露了一些有用的实例属性与方法.它们都有前缀 $,以便与用户定义的属性区分开来 var data = { a: 1 } var vm = new Vue({ el: ' ...
- Mybatis精讲(二)---生命周期
目录 回顾 SqlSessionFactoryBuilder SqlSessionFactory openSessionFromDataSource Executor SqlSession Mappe ...
- Spring注解驱动开发(二)-----生命周期、属性赋值
bean的生命周期 bean的生命周期:bean创建---初始化----销毁的过程容器管理bean的生命周期:我们可以自定义初始化和销毁方法:容器在bean进行到当前生命周期的时候来调用我们自定义的初 ...
- VUE二 生命周期详解
vue官网对vue生命周期的介绍 Vue实例有一个完整的生命周期,也就是从开始创建.初始化数据.编译模板.挂载Dom.渲染→更新→渲染.销毁等一系列过程,我们称这是Vue的生命周期.通俗说就是Vue实 ...
随机推荐
- DB_MYSQL_mysql-5.7.10-winx64解压版安装笔记
1.http://dev.mysql.com/downloads/mysql/ 里面下载Windows (x86, 64-bit), ZIP Archive mysql-5.7.10-winx64.z ...
- Linux安装gcc编译器详解
本人使用的是CentOS 6.5 64位系统,由于在安装系统的时候并没有勾选安装gcc编译器,因此需要自行安装gcc编译器. 使用yum安装gcc 对于配备了yum的Linux发行版而言,安装gcc编 ...
- Spring-MVC开发之全局异常捕获全面解读
异常,异常 我们一定要捕获一切该死的异常,宁可错杀一千也不能放过一个! 产品上线后的异常更要命,一定要屏蔽错误内容,以免暴露敏感信息! 在用Spring MVC开发WEB应用时捕获全局异常的方法基本有 ...
- VS问题:该依赖项是由项目系统添加的,不能删除。
该依赖项是由项目系统添加的,不能删除. 原因:是该项目添加对依赖项的引用时,不是直接引用的dll,而是通过“添加引用->项目”的方式引用的项目. 解决:删除“引用”目录下该依赖项的引用,然后通过 ...
- spring + spring mvc可能会遇到的问题
Spring容器优先加载由ServletContextListener(对应applicationContext.xml)产生的父容器,而SpringMVC(对应mvc_dispatcher_serv ...
- ADF_Starting系列9_使用EJB/JPA/JSF通过ADF构建Web应用程序之测试J2EE Container
2013-05-01 Created By BaoXinjian
- 高并发分布式系统中生成全局唯一Id汇总
数据在分片时,典型的是分库分表,就有一个全局ID生成的问题.单纯的生成全局ID并不是什么难题,但是生成的ID通常要满足分片的一些要求: 1 不能有单点故障. 2 以时间为序,或者ID里包含时间 ...
- DataTable排序(转)
DataTable 排序 DataRow[] rows = dataTable1.Select("", "ord asc"); DataTable t ...
- TNS-12547 Linux Error: 104: Connection reset by pe (转载)
TNS-12547 Linux Error: 104: Connection reset by peer 解决过程参考:http://blog.chinaunix.net/u/7121/showart ...
- PHP文件读写操作之文件写入代码
在PHP网站开发中,存储数据通常有两种方式,一种以文本文件方式存储,比如txt文件,一种是以数据库方式存储,比如Mysql,相对于数据库存储,文件存储并没有什么优势,但是文件读写操作在基本的PHP开发 ...