[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,里面的文章写的蛮好的,大家可以查考。
[IoC容器Unity]第二回:Lifetime Managers生命周期的更多相关文章
- [转载][IoC容器Unity]第二回:Lifetime Managers生命周期
1.引言 Unity的生命周期是注册的类型对象的生命周期,而Unity默认情况下会自动帮我们维护好这些对象的生命周期,我们也可以显示配置对象的生命周期,Unity将按照配置自动管理,非常方便,下面就介 ...
- [IoC容器Unity]第四回:使用范例
1.引言 前面几个章节介绍了Unity的基本使用,主要分为程序和配置文件两种方法的使用,可以参考一下链接, [IoC容器Unity]第一回:Unity预览 [IoC容器Unity]第二回:Lifeti ...
- [IoC容器Unity]第三回:依赖注入
1.引言 上节介绍了,Unity的Lifetime Managers生命周期,Unity具体实现依赖注入包含构造函数注入.属性注入.方法注入,所谓注入相当赋值,下面一个一个来介绍. 2.构造函数注入 ...
- IOC容器Unity的使用及独立配置文件Unity.Config
[本段摘录自:IOC容器Unity 使用http://blog.csdn.net/gdjlc/article/details/8695266] 面向接口实现有很多好处,可以提供不同灵活的子类实现,增加 ...
- [IoC容器Unity] :Unity预览
1.引言 高内聚,低耦合成为一个OO架构设计的一个参考标准.高内聚是一个模块或者一个类中成员跟这个模块或者类的关系尽量高,低耦合是不同模块或者不同类之间关系尽量简单. 拿咱国家举例来说,假如你是中国人 ...
- [IoC容器Unity]第一回:Unity预览
1.引言 高内聚,低耦合成为一个OO架构设计的一个参考标准.高内聚是一个模块或者一个类中成员跟这个模块或者类的关系尽量高,低耦合是不同模块或者不同类之间关系尽量简单. 拿咱国家举例来说,假如你是中国人 ...
- Autofac容器对象实例的几种生命周期类型
实例范围决定了如何在同一服务的请求之间共享实例. 请注意,您应该熟悉生命周期范围的概念,以便更好地理解此处发生的情况. 当请求服务时,Autofac可以返回单个实例(单实例作用域),新实例(每个依赖作 ...
- 018-019 NET5_内置容器支持依赖注入+IServiceCollection的生命周期
概念: DI依赖注入: IServiceCollection仅支持构造函数注入 什么是依赖注入? 如果对象A依赖对象B,对象B依赖对象C,就可以先构造对象C,然后传递给对象B,再把对象B传递给A.得到 ...
- ASP.NET Core :容器注入(二):生命周期作用域与对象释放
//瞬时生命周期 ServiceCollection services = new ServiceCollection(); services.AddTransient<TestServiceI ...
随机推荐
- AWS的EC2实例搭建服务器使用stackoverflow教程
作为一个技术开发工程师, 一个给力的问题解决方案搜索引擎是十分必要的, stackoverflow作为一个码农必备神器, 存在访问不稳定,有时候打不开的问题,下面介绍如何在亚马逊云服务器上搭建属于自己 ...
- Appium IOS 使用多模拟器并发执行测试
申明一下 转载请注明出处 复制粘贴请滚蛋 !!!!!!!! 最近在是用appium进行app的并发测试,并且Android已经实现在同一台PC机使用多个模拟器并发测试的功能 这里说一句模拟器使 ...
- boost中打印python中的变量
p::extract<char const *>(p::str(py_variable))
- 【Linux】Mac Centos install VMware Tools
can't use yum: vi /etc/sysconfig/network-scripts/ifcfg-enp4s0 yum -y install lshw pciutils gdisk sys ...
- 【Eclipse】-NO.163.Eclipse.1 -【Eclipse springboot 1.x 创建maven工程初始化报错】
Style:Mac Series:Java Since:2018-09-10 End:2018-09-10 Total Hours:1 Degree Of Diffculty:5 Degree Of ...
- 使用commons.cli实现MyCP
目录 Commons.cli库 MyCP 测试代码 总结 Commons.cli库 考虑到这次的任务是实现自己的命令行命令cp,我认为简单地使用args[]无法很好的完成需求.经过网上的一番搜索,我找 ...
- GIT回滚master分支到指定tag版本
master版本上线以后一般要打一个tag备份,以防事态有变,这是一个好习惯,如果以后有问题也可以放心的回滚版本,那么怎么用tag版本覆盖mastaer呢,其实只有几个命令 1.查看分支 git br ...
- virtual dom 简单了解
管理应用程序状态和用户界面的同步一直是前端UI开发复杂性的主要来源.目前出现了不同的方式来处理这个问题.本文简单讨论其中一种方式virtual dom. 文章概要: virtual dom 基本概念, ...
- PHP----------php封装的一些简单实用的方法汇总
1.xml转换成array,格式不对的xml则返回false function xml_parser($str){ $xml_parser = xml_parser_create(); i ...
- Shadow DOM及自定义标签
参考链接:点我 一.什么是Shadow DOM Shadow DOM,直接翻译的话就是 影子 DOM,可以理解为潜藏在 DOM 结构中并且我们无法直接控制操纵的 DOM 结构.类似于下面这种结构 Sh ...