1、前言
前面的博客主要介绍了Autofac
中的一些注册方法,下面就来介绍一下Autofac
中实例的生命周期。之前在介绍ASP.NET Core
内置IoC
容器的时候说过,实例的生命周期有:瞬时生命周期
、域生命周期
、全局单例生命周期
,而Autofac
在这三种周期之上又新增了若干周期模式,下面开始介绍。
2、Autofac中的生命周期
2.1、InstancePerDependency
InstancePerDependency
表示瞬时生命周期,它是Autofac
中默认的周期模式,在瞬时生命周期中,容器每次都会创建新的实例,代码如下:
using Autofac;
using System;namespace App
{internal class Program{static void Main(string[] args){// 注册接口和类ContainerBuilder builder = new ContainerBuilder();builder.RegisterType<Cat>().As<IAnimal>().InstancePerDependency();// 构建容器IContainer container = builder.Build();// scope1using (var scope = container.BeginLifetimeScope()){IAnimal obj1 = scope.Resolve<IAnimal>();IAnimal obj2 = scope.Resolve<IAnimal>();Console.WriteLine(obj1.GetHashCode());Console.WriteLine(obj2.GetHashCode());}Console.WriteLine("---------");// scope2using (var scope = container.BeginLifetimeScope()){IAnimal obj3 = scope.Resolve<IAnimal>();IAnimal obj4 = scope.Resolve<IAnimal>();Console.WriteLine(obj3.GetHashCode());Console.WriteLine(obj4.GetHashCode());}}}
}
运行结果如下所示:
43527150
56200037
---------
36038289
55909147
可以发现:obj1
、obj2
、obj3
、obj4
的哈希值均不相同,因此它们是不同的实例。
2.2、InstancePerLifetimeScope
InstancePerLifetimeScope
表示域生命周期。域周期表示在同一个域中,每个实例都是相同的,而不同域中的实例又是不同的,因此可以理解为在域中实现了单例模式,代码如下:
using Autofac;
using System;namespace App
{internal class Program{static void Main(string[] args){// 注册接口和类ContainerBuilder builder = new ContainerBuilder();builder.RegisterType<Cat>().As<IAnimal>().InstancePerLifetimeScope();// 构建容器IContainer container = builder.Build();// scope1using (var scope = container.BeginLifetimeScope()){IAnimal obj1 = scope.Resolve<IAnimal>();IAnimal obj2 = scope.Resolve<IAnimal>();Console.WriteLine(obj1.GetHashCode());Console.WriteLine(obj2.GetHashCode());}Console.WriteLine("---------");// scope2using (var scope = container.BeginLifetimeScope()){IAnimal obj3 = scope.Resolve<IAnimal>();IAnimal obj4 = scope.Resolve<IAnimal>();Console.WriteLine(obj3.GetHashCode());Console.WriteLine(obj4.GetHashCode());}}}
}
运行结果如下所示:
43527150
43527150
---------
56200037
56200037
可以发现:obj1
与obj2
的哈希值相同,obj3
与obj4
的哈希值相同,因此obj1
与obj2
为同一实例,obj3
与obj4
为同一实例。
2.3、InstancePerMatchingLifetimeScope
InstancePerMatchingLifetimeScope
也表示域生命周期,它允许开发者做更精细的控制,那么它与InstancePerLifetimeScope
的区别在哪里?来看下面一段代码:
using Autofac;
using System;namespace App
{internal class Program{static void Main(string[] args){// 注册接口和类ContainerBuilder builder = new ContainerBuilder();builder.RegisterType<Cat>().As<IAnimal>().InstancePerLifetimeScope();// 构建容器IContainer container = builder.Build();// scope1using (var scope = container.BeginLifetimeScope()){IAnimal obj1 = scope.Resolve<IAnimal>();IAnimal obj2 = scope.Resolve<IAnimal>();Console.WriteLine(obj1.GetHashCode());Console.WriteLine(obj2.GetHashCode());Console.WriteLine("---------");// 子域using (var scope1 = scope.BeginLifetimeScope()){IAnimal obj3 = scope1.Resolve<IAnimal>();IAnimal obj4 = scope1.Resolve<IAnimal>();Console.WriteLine(obj3.GetHashCode());Console.WriteLine(obj4.GetHashCode());}}}}
}
上面的代码设置为InstancePerLifetimeScope
模式,在域scope
中又创建了一个子域scope1
,运行结果如下:
43527150
43527150
---------
56200037
56200037
可以发现:子域中的obj3
和obj4
的哈希值相同,但它们与obj1
和obj2
的哈希值却不相同。因此obj1
与obj2
为同一实例,obj3
与obj4
为同一实例。现在切换为InstancePerMatchingLifetimeScope
模式,代码如下:
using Autofac;
using System;namespace App
{internal class Program{static void Main(string[] args){// 注册接口和类ContainerBuilder builder = new ContainerBuilder();builder.RegisterType<Cat>().As<IAnimal>().InstancePerMatchingLifetimeScope("abc");// 构建容器IContainer container = builder.Build();// scope1using (var scope = container.BeginLifetimeScope("abc")){IAnimal obj1 = scope.Resolve<IAnimal>();IAnimal obj2 = scope.Resolve<IAnimal>();Console.WriteLine(obj1.GetHashCode());Console.WriteLine(obj2.GetHashCode());Console.WriteLine("---------");// 子域using (var scope1 = scope.BeginLifetimeScope()){IAnimal obj3 = scope1.Resolve<IAnimal>();IAnimal obj4 = scope1.Resolve<IAnimal>();Console.WriteLine(obj3.GetHashCode());Console.WriteLine(obj4.GetHashCode());}}}}
}
上面的代码在注册时打了一个标签,名称为abc
,然后通过该标签名称创建域scope
,运行结果如下所示:
43527150
43527150
---------
43527150
43527150
可以发现:obj1
、obj2
、obj3
、obj4
的哈希值均相同,它们为同一实例。因此可以这样理解:如果将生命周期设置为InstancePerMatchingLifetimeScope
模式,那么在该域中,不管创建了多少子域,它们都会调用同一实例。
2.4、InstancePerRequest
InstancePerRequest
表示在每次HTTP
请求内实现单例。该周期模式只适用于Web
项目,由于本文的例子基于控制台程序,因此不太方便举例说明。本质上这也是一种域内单例的周期模式,但并不经常使用。
2.5、InstancePerOwned
关于InstancePerOwned
周期,相关的介绍不多,我在这里就凭借自己的一点理解来进行说明,如果有不对的地方还请大家指出来。首先新增一个类Zoo
,代码如下:
namespace App
{public class Zoo{protected readonly IAnimal _animal;public Zoo(IAnimal animal){_animal = animal;}public string Get(){return _animal.GetMsg();}}
}
然后在Autofac
中注册该类,代码如下:
using Autofac;
using System;namespace App
{internal class Program{static void Main(string[] args){// 注册接口和类ContainerBuilder builder = new ContainerBuilder();builder.RegisterType<Zoo>();builder.RegisterType<Cat>().As<IAnimal>();// 构建容器IContainer container = builder.Build();// scope1using (var scope = container.BeginLifetimeScope()){var cat = scope.Resolve<IAnimal>();Console.WriteLine(cat.GetMsg());var zoo = scope.Resolve<Zoo>();Console.WriteLine(zoo.Get());}}}
}
运行结果如下所示:
This is cat
This is cat
上面这段代码很简单,现在把代码改一下,把InstancePerOwned
周期加进来:
using Autofac;
using System;namespace App
{internal class Program{static void Main(string[] args){// 注册接口和类ContainerBuilder builder = new ContainerBuilder();builder.RegisterType<Zoo>();builder.RegisterType<Cat>().As<IAnimal>().InstancePerOwned<Zoo>();// 构建容器IContainer container = builder.Build();// scopeusing (var scope = container.BeginLifetimeScope()){var cat = scope.Resolve<IAnimal>();Console.WriteLine(cat.GetMsg());var zoo = scope.Resolve<Zoo>();Console.WriteLine(zoo.Get());}}}
}
运行代码,程序报错,发现在创建Cat
实例的时候发生异常,如下图所示:
这是因为InstancePerOwned
模式会对Cat
实例的创建做出限制:
builder.RegisterType<Cat>().As<IAnimal>().InstancePerOwned<Zoo>();
根据我对官方文档的理解,上面这行代码可以理解为:Cat
实例的创建依赖于Zoo
实例的创建。当Zoo
实例被创建后,容器会自动创建Cat
实例,同时在域scope
内无法单独使用Cat
实例,它只能在Zoo
实例的内部进行调用。因此开发者无法在域scope
内手动调用Resolve
方法生成Cat
实例。下面修改一下代码:
using Autofac;
using Autofac.Features.OwnedInstances;
using System;namespace App
{internal class Program{static void Main(string[] args){// 注册接口和类ContainerBuilder builder = new ContainerBuilder();builder.RegisterType<Zoo>();builder.RegisterType<Cat>().As<IAnimal>().InstancePerOwned<Zoo>();// 构建容器IContainer container = builder.Build();// scopeusing (var scope = container.BeginLifetimeScope()){var zoo = scope.Resolve<Owned<Zoo>>();Console.WriteLine(zoo.Value.Get());zoo.Dispose();}}}
}
运行结果如下所示:
This is cat
在使用InstancePerOwned
周期时,我们需要使用Owned<>
来接收容器创建的实例,然后对Value
属性进行操作。在上面的代码中,当调用Resolve<Owned<Zoo>>
时,Cat
实例就会被自动创建。但无法直接调用。对象使用完后需要手动调用Dispose
方法对其进行销毁。
2.6、SingleInstance
SingleInstance
表示全局单例生命周期,这个比较好理解,即:所有创建的实例均为同一实例,代码如下:
using Autofac;
using System;namespace App
{internal class Program{static void Main(string[] args){// 注册接口和类ContainerBuilder builder = new ContainerBuilder();builder.RegisterType<Cat>().As<IAnimal>().SingleInstance();// 构建容器IContainer container = builder.Build();// scope1using (var scope = container.BeginLifetimeScope()){IAnimal obj1 = scope.Resolve<IAnimal>();IAnimal obj2 = scope.Resolve<IAnimal>();Console.WriteLine(obj1.GetHashCode());Console.WriteLine(obj2.GetHashCode());}Console.WriteLine("---------");// scope2using (var scope = container.BeginLifetimeScope()){IAnimal obj3 = scope.Resolve<IAnimal>();IAnimal obj4 = scope.Resolve<IAnimal>();Console.WriteLine(obj3.GetHashCode());Console.WriteLine(obj4.GetHashCode());}}}
}
运行结果如下所示:
43527150
43527150
---------
43527150
43527150
3、结语
本文主要介绍了Autofac
中实例的生命周期。一般情况下,瞬时
、域
、单例
三种周期模式应用较多,而其他的周期模式则相对较少,有兴趣的同志可以查看Autofac
官方文档进行深入了解。