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. 某里巴巴Java工程师常规面试题以及解答

    从HR弄来的P6-P7的JAVA工程师题目,分享给大家 1 Spring AOP和IOC的实现方法 http://blog.csdn.net/tarena_lixy/article/details/7 ...

  2. 【Docker】-NO.132.Docker.1 -【Docker 修改容器端口】

    Style:Mac Series:Java Since:2018-09-10 End:2018-09-10 Total Hours:1 Degree Of Diffculty:5 Degree Of ...

  3. nginx隐藏tp路由index.php

    ---------------------------------------------- 也可以找到vhosts.conf文件修改, 重启nginx,service nginx restart

  4. CarbonData-1:common

    最近公司需要对CarbonData进一步应用,或许封装进产品,或许是为了解析CarbonData元数据,于是开始预研CarbonData,下面将保持每天一篇以上的阅读CarbonData源码博客,由于 ...

  5. Linux.ls 查看常用参数

    Linux.ls 查看常用参数 在linux中ls查看文件常用参数: -l :详情文件 -h : 格式化文件大小 -r :将查询的结果列表进行翻转 -S :按文件大小进行排列

  6. servlet中如何实现通过Spring实现对象的注入

    @WebServlet("/BaseServlet")public class BaseServlet extends HttpServlet { private static f ...

  7. apache24虚拟安装

    1.进入Apache的conf目录 2.打开httpd.conf文件输入: 2.1:查找<IfModule alias_module> 2.2:    在     ScriptAlias ...

  8. sql 生成随机字符串

    生成三位随机字母+12位数字 ),), @c int; select @CardCode=abs(CHECKSUM(NEWID())) -LEN(@CardCode); ,@c)) set @Card ...

  9. day16 python之匿名函数,递归函数

    匿名函数 匿名函数格式 函数名 = lambda 参数 :返回值 #参数可以有多个,用逗号隔开 #匿名函数不管逻辑多复杂,只能写一行,且逻辑执行结束后的内容就是返回值 #返回值和正常的函数一样可以是任 ...

  10. ubuntu18.04 安装pip3

    Ubuntu18.04默认内嵌python2.python3,pip安装时,python2对应安装pip,python3对应安装pip3. sudo apt install python3-pip 检 ...