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进行对象关系注册时如果没有指定生命周期管理器则默认使用这个生命周期管理器,这个生命周期管理器就如同其名字一样,当使用这种管理器的时候,每次通过ResolveResolveAll调用对象的时候都会重新创建一个新的对象。

需要注意的是,使用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生命周期的更多相关文章

  1. [转载][IoC容器Unity]第二回:Lifetime Managers生命周期

    1.引言 Unity的生命周期是注册的类型对象的生命周期,而Unity默认情况下会自动帮我们维护好这些对象的生命周期,我们也可以显示配置对象的生命周期,Unity将按照配置自动管理,非常方便,下面就介 ...

  2. [IoC容器Unity]第四回:使用范例

    1.引言 前面几个章节介绍了Unity的基本使用,主要分为程序和配置文件两种方法的使用,可以参考一下链接, [IoC容器Unity]第一回:Unity预览 [IoC容器Unity]第二回:Lifeti ...

  3. [IoC容器Unity]第三回:依赖注入

    1.引言 上节介绍了,Unity的Lifetime Managers生命周期,Unity具体实现依赖注入包含构造函数注入.属性注入.方法注入,所谓注入相当赋值,下面一个一个来介绍. 2.构造函数注入 ...

  4. IOC容器Unity的使用及独立配置文件Unity.Config

    [本段摘录自:IOC容器Unity 使用http://blog.csdn.net/gdjlc/article/details/8695266] 面向接口实现有很多好处,可以提供不同灵活的子类实现,增加 ...

  5. [IoC容器Unity] :Unity预览

    1.引言 高内聚,低耦合成为一个OO架构设计的一个参考标准.高内聚是一个模块或者一个类中成员跟这个模块或者类的关系尽量高,低耦合是不同模块或者不同类之间关系尽量简单. 拿咱国家举例来说,假如你是中国人 ...

  6. [IoC容器Unity]第一回:Unity预览

    1.引言 高内聚,低耦合成为一个OO架构设计的一个参考标准.高内聚是一个模块或者一个类中成员跟这个模块或者类的关系尽量高,低耦合是不同模块或者不同类之间关系尽量简单. 拿咱国家举例来说,假如你是中国人 ...

  7. Autofac容器对象实例的几种生命周期类型

    实例范围决定了如何在同一服务的请求之间共享实例. 请注意,您应该熟悉生命周期范围的概念,以便更好地理解此处发生的情况. 当请求服务时,Autofac可以返回单个实例(单实例作用域),新实例(每个依赖作 ...

  8. 018-019 NET5_内置容器支持依赖注入+IServiceCollection的生命周期

    概念: DI依赖注入: IServiceCollection仅支持构造函数注入 什么是依赖注入? 如果对象A依赖对象B,对象B依赖对象C,就可以先构造对象C,然后传递给对象B,再把对象B传递给A.得到 ...

  9. ASP.NET Core :容器注入(二):生命周期作用域与对象释放

    //瞬时生命周期 ServiceCollection services = new ServiceCollection(); services.AddTransient<TestServiceI ...

随机推荐

  1. MySQL数据库常用命令和概念 (1)

    一.数据库的创建: 1.创建一个名称为mydb1的数据库 create database mydb1; 2.创建一个使用utf8字符集的mydb2数据库. create database mydb2 ...

  2. python 模拟实现一个ATM + 购物商城程序

    思路:ATM是一个单独程序,提供给消费的是一个接口core下的settlement.py,只做了个人的,没写管理的模块 Shopping也是一个单独的,只做了一个购物的消费模块,没写商家模块,偷懒用了 ...

  3. NetCore +EF+Mysql 从数据库生成实体类到项目

    1.点击“工具”->“NuGet包管理器”->“程序包管理器控制台” 分别安装以下几个包 Mysql 版本: Install-Package MySql.Data.EntityFramew ...

  4. rpgmakermv \c 常用颜色一览

    1 2 3 4 5 6 7 14 18

  5. ASP.NET MVC案例教程(六)

    ASP.NET六 一个小难题 我们继续完善“MVC公告发布系统”,这次,我们的需求是对公告发布功能添加日志记录能力,即在发布公告前,记录一次,在公告发布成功后,再记录一次.然后还要使得其具备异常处理, ...

  6. shell里连接数据库,将结果输出到变量

    result=$(sqlplus -s 'ccc/ccc@21.96.34.34:1521'<<EOF ..... EOF )

  7. 安装percona-toolkit工具时遇到的问题

    1. 从这个链接https://www.percona.com/doc/percona-toolkit/3.0/index.html下载percona-toolkit安装包 2. 下载完成通过ftp工 ...

  8. mui返回上个页面并刷新数据

    转 https://blog.csdn.net/mercedescc/article/details/82769264 今天写项目遇到个问题,就是B页面支付操作完成以后,点击返回按钮要到A页面,此时A ...

  9. activemq修改端口

    1.修改TCP 61616端口 cd /apps/svr/activemq/conf cat activemq.xml |grep transportConnector <transportCo ...

  10. CSS——div垂直居中及div内文字垂直居中

    最近做demo时,经常需要div垂直居中或者让div内文字相对div垂直居中.水平居中比较简单,就不多说了,这里主要记录一下垂直居中的一些方法. 一.div垂直居中的一些方法: 1.当height.w ...