最通俗易懂的依赖注入之服务容器与作用域
Posted dotNET跨平台
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最通俗易懂的依赖注入之服务容器与作用域相关的知识,希望对你有一定的参考价值。
推荐关注「码侠江湖」加星标,时刻不忘江湖事
这篇文章是 ASP.NET 6 依赖注入系列文章的第 3 篇,点击上方蓝字可以阅读整个系列。
在上一篇文章中,我们讨论依赖注入的基本用法与生命周期。
接下来,在这篇文章中,我们继续深入了解服务容器相关的概念。
服务容器
我们回顾一下什么是服务容器。
在上一篇文章中,我们提到过,依赖注入系统中的服务容器会保存,由依赖注入系统创建的具有有效生命周期的服务实例。
「在 ASP.NET 的依赖注入系统中,服务容器有根容器和子容器之分。」
第一个在依赖注入系统中创建出来的容器就是根容器,而且只会有一个根容器存在,子容器可以有很多个。
它们的区别在于:
「根容器中保存的实例对于所有子容器都是可见的。」
「子容器中保存的的实例互相不可见。」
「不同生命周期模式的服务实例,会被保存在不同的容器里。」
单例模式的服务实例被保存在根容器中。
作用域模式的服务实例被保存在作用域的容器中,作用域的容器就是子容器。
由于子容器互相不可见,所以作用域模式中保存的实例,只会在本作用域中生存。
瞬时模式的服务实例不会被保存在任何容器中的。
子容器示例
我们来看这个示例,演示了三种不同生命周期模式的区别:
public interface IAccount
public interface IMessage
public interface ITool
public class Base
public Base()
Console.WriteLine($"Created:GetType().Name");
public class Account: Base, IAccount
public class Message:Base, IMessage
public class Tool:Base, ITool
public static void Main()
// 创建服务集合
var serviceCollection = new ServiceCollection()
.AddTransient<IAccount, Account>()
.AddScoped<IMessage, Message>()
.AddSingleton<ITool, Tool>();
// 创建 ServiceProvider 对象代表的服务根容器
var root = serviceCollection.BuildServiceProvider();
// 创建子容器1
var child1 = root.CreateScope().ServiceProvider;
// 创建子容器2
var child2 = root.CreateScope().ServiceProvider;
// 获取子容器1的服务实例
GetService<IAccount>(child1);
GetService<IMessage>(child1);
GetService<ITool>(child1);
Console.WriteLine();
// 获取子容器2的服务实例
GetService<IAccount>(child2);
GetService<IMessage>(child2);
GetService<ITool>(child2);
// 为验证生命周期,连续获取两次
public static void GetService<T>(IServiceProvider provider)
provider.GetService<T>();
provider.GetService<T>();
创建容器的代码与上一篇文章中的示例相同,这里我们主要关注两个 CreateScope 方法的使用:
var root = serviceCollection.BuildServiceProvider();
var child1 = root.CreateScope().ServiceProvider;
var child2 = root.CreateScope().ServiceProvider;
通过使用根容器的 CreateScope 方法,可以创建出 ServiceScope 对象,它就代表了服务的作用域。
每个作用域中,都有一个 ServiceProvider 对象,它代表服务容器。
我们可以通过服务根容器,去创建它的子容器。
最后,我们通过两个子容器去获取相应的服务实例:
// 获取子容器1的服务实例
GetService<IAccount>(child1);
GetService<IMessage>(child1);
GetService<ITool>(child1);
Console.WriteLine();
// 获取子容器2的服务实例
GetService<IAccount>(child2);
GetService<IMessage>(child2);
GetService<ITool>(child2);
Base 类中的构造函数,会在控制台中打印被创建的具体实例,因此这个示例的运行结果如下:
通过结果可以发现:
Account 服务的生命周期为瞬时,所以两次获取该服务的实例时,都会创建一个全新的 Account 对象。
Message 服务的生命周期是作用域,如果在同一个子容器中去获取它的服务实例,那么只会创建一个 Message 对象,而这里有两个子容器,所以创建了两个 Message 对象。
Tool 服务的生命周期是单例,单例模式的服务实例,只保存在根容器上,并且对所有子容器可见。由于两个子容器拥有同样的根容器,所以只有第一次获取时 Tool 对象才会被创建。
服务作用域
每一个新创建的服务作用域ServiceScope
对象都具有一个ServiceProvider
对象,它代表的是服务容器。
「根容器可以创建子容器,子容器也可以创建子容器。」
但所有的子容器都是平级的,也就是说无论它是由谁创建的,它都是根容器的子容器,而不存在所谓的“孙子容器”。
虽然根容器与子容器是父子关系,但其实「子容器并不知道自己的父容器是谁,它们只知道根容器是谁」。
在子容器的眼中,只有君没有父。
对于 ASP.NET 应用来说,服务作用域具有非常明确的边界,也就是每个 HTTP 的请求上下文。
也就是说,服务作用域的生命周期与每个请求上下文是绑定在一起的。
我们现在知道,容器有根容器和子容器之分。
其实根容器和子容器也有不同的身份,我们来看这张图:
「顶端的是根容器,也称应用容器。」
「分支都是子容器,它们都是根据请求创建和释放的子容器,也称请求容器。」
在 ASP.NET 应用初始化过程中,会用到大量的内置服务实例,而这些服务实例都是由应用容器提供。
在具体处理某个请求的时候,ASP.NET 框架会针对当前请求,创建一个服务作用域对象。
这个服务作用域对象中的请求容器,用来提供当前请求处理过程中所需的服务实例。
更多精彩内容,请关注我▼▼
如果喜欢我的文章,那么
在看和转发是对我最大的支持!
(戳下面蓝字阅读)
推荐关注微信公众号:码侠江湖
觉得不错,点个在看再走哟
阅读世界,共赴山海 423全民读书节,邀你共读以上是关于最通俗易懂的依赖注入之服务容器与作用域的主要内容,如果未能解决你的问题,请参考以下文章