生命周期决定了 IServiceProvider 对象采用怎样的方式提供和释放服务实例. 虽然不同版本的依赖注入框架针对服务实例的生命周期管理采用了不同的实现, 但总的来说原理还是类似的. 在我们提供的依赖注入框架 Cat 中, 我们已经模拟了三种生命周期模式的实现原理, 接下来我们结合 "服务范围" 的概念来对这个话题做进一步讲述.
一, 服务范围(Service Scope)
对于依赖注入框架采用的三种生命周期模式 (Singleton,Scoped 和 Transient) 来说, Singleton 和 Transient 都具有明确的语义, 但是 Scoped 代表一种怎样的生命周期模式, 很多初学者往往搞不清楚. 这里所谓的 Scope 指的是由 IServiceScope 接口表示的 "服务范围", 该范围由 IServiceScopeFactory 接口表示的 "服务范围工厂" 来创建. 如下面的代码片段所示, IServiceProvider 的扩展方法 CreateScope 正是利用提供的 IServiceScopeFactory 服务实例来创建作为服务范围的 IServiceScope 对象.
- public interface IServiceScope : IDisposable
- {
- IServiceProvider ServiceProvider { get; }
- }
- public interface IServiceScopeFactory
- {
- IServiceScope CreateScope();
- }
- public static class ServiceProviderServiceExtensions
- {
- public static IServiceScope CreateScope(this IServiceProvider provider) => provider.GetRequiredService<IServiceScopeFactory>().CreateScope();
- }
任何一个 IServiceProvider 对象都可以利用其注册的 IServiceScopeFactory 服务创建一个代表服务范围的 IServiceScope 对象, 后者代表的 "范围" 内具有一个新创建的 IServiceProvider 对象(对应着接口 IServiceScope 的 ServiceProvider 属性), 该对象与当前 IServiceProvider 在逻辑上具有如下图所示的 "父子关系".
如上图所示的树形层次结构只是一种逻辑结构, 从对象引用层面来看, 通过某个 IServiceScope 封装的 IServiceProvider 对象不需要知道自己的 "父亲" 是谁, 它只关心作为根节点的 IServiceProvider 在哪里就可以了. 下图从物理层面揭示了 IServiceScope / IServiceProvider 对象之间的关系, 任何一个 IServiceProvider 对象都具有针对根容器的引用.
二, 服务实例的提供
只有在充分了解 IServiceScope 对象的创建过程以及它与 IServiceProvider 对象之间的关系之后, 我们才会对三种生命周期管理模式 (Singleton,Scoped 和 Transient) 具有深刻的认识. 就服务实例的提供方式来说, 它们之间具有如下的差异:
Singleton:IServiceProvider 对象创建的服务实例保存在作为根容器的 IServiceProvider 对象上, 所以多个同根的 IServiceProvider 对象提供的针对同一类型的服务实例都是同一个对象.
Scoped:IServiceProvider 对象创建的服务实例由自己保存, 所以同一个 IServiceProvider 对象提供的针对同一类型的服务实例均是同一个对象.
Transient: 针对每一次服务提供请求, IServiceProvider 对象总是创建一个新的服务实例.
三, 服务实例的释放
IServiceProvider 除了为我们提供所需的服务实例之外, 对于由它提供的服务实例, 它还肩负起回收释放的责任. 这里所说的回收释放与. NET Core 自身的垃圾回收机制无关, 仅仅针对于自身类型实现了 IDisposable 或者 IAsyncDisposable 接口的服务实例(下面简称为 Disposable 服务实例), 针对服务实例的释放体现为调用它们的 Dispose 或者 DisposeAsync 方法. IServiceProvider 对象针对服务实例采用的回收释放策略取决于采用的生命周期模式, 具体策略主要体现为如下两点:
Singleton: 提供 Disposable 服务实例保存在作为根容器的 IServiceProvider 对象上, 只有在这个 IServiceProvider 对象被释放的时候这些 Disposable 服务实例才能被释放.
Scoped 和 Transient: 当前 IServiceProvider 对象会保存由它提供的 Disposable 服务实例, 当自己被释放的时候, 这些 Disposable 服务实例就会被释放.
综上所述, 每个作为依赖注入容器的 IServiceProvider 对象都具有如下图所示的两个列表来存放服务实例, 我们将它们分别命名为 "Realized Services" 和 "Disposable Services", 对于一个作为非根容器的 IServiceProvider 对象来说, 由它提供的 Scoped 服务保存在自身的 Realized Services 列表中, Singleton 服务实例则会保存在根容器的 Realized Services 列表中. 如果服务实现类型实现了 IDisposable 或者 IAsyncDisposable 接口, Scoped 和 Transient 服务实例会被保存到自身的 Disposable Services 列表中, 而 Singleton 服务实例则会保存到根容器的 Disposable Services 列表中.
对于作为根容器的 IServiceProvider 对象来说, Singleton 和 Scoped 模式对它来说是两种等效的生命周期模式, 由它提供的 Singleton 和 Scoped 服务实例会被存放到自身的 Realized Services 列表中, 而所有需要被释放的服务实例则被存放到 Disposable Services 列表中. 当某个 IServiceProvider 对象被用于提供针对指定类型的服务实例时, 它会根据服务类型提取出表示服务注册的 ServiceDescriptor 对象并根据它得到对应的生命周期模式:
如果生命周期模式为 Singleton, 并且作为根容器的 Realized Services 列表中包含对应的服务实例, 它将作为最终提供的服务实例. 如果这样的服务实例尚未创建, 那么新的服务将会被创建出来并作为提供的服务实例. 这个服务实例会被添加到根容器的 Realized Services 列表中. 如果实例类型实现了 IDisposable 或者 IAsyncDisposable 接口, 创建的服务实例会被添加到根容器的 Disposable Services 列表中.
如果生命周期为 Scoped, 那么 IServiceProvider 会先确定自身的 Realized Services 列表中是否存在对应的服务实例, 存在的服务实例将作为最终的返回值. 如果 Realized Services 列表不存在对应的服务实例, 那么新的服务实例会被创建出来. 在作为最终的服务实例被返回之前, 创建的服务实例会被添加到自身的 Realized Services 列表中, 如果实例类型实现了 IDisposable 或者 IAsyncDisposable 接口, 创建的服务实例会被添加到自身的 Disposable Services 列表中.
如果提供服务的生命周期为 Transient, 那么 IServiceProvider 会直接创建一个新的服务实例. 在作为最终的服务实例被返回之前, 创建的服务实例会被添加到自身的 Realized Services 列表中, 如果实例类型实现了 IDisposable 或者 IAsyncDisposable 接口, 创建的服务实例会被添加到自身的 Disposable Services 列表中.
对于非根容器的 IServiceProvider 对象来说, 它的生命周期是由 "包裹" 着它的 IServiceScope 对象控制的. 从前面给出的定义可以看出 IServiceScope 实现了 IDisposable 接口, Dispose 方法的执行不仅标志着当前服务范围的终结, 也意味着对应 IServiceProvider 对象生命周期的结束.
当代表服务范围的 IServiceScope 对象的 Dispose 方法被调用的时候, 它会调用对应 IServiceProvider 对象的 Dispose 方法. 一旦 IServiceProvider 对象因自身 Dispose 方法的调用而被释放的时候, 它会从自身的 Disposable Services 列表中提取出所有需要被释放的服务实例, 并调用它们的 Dispose 或者 DisposeAsync 方法. 在这之后, Disposable Services 和 Realized Services 列表会被清空, 列表中的服务实例和 IServiceProvider 对象自身会成为垃圾对象被 GC 回收.
四, ASP.NET Core 应用
依赖注入框架所谓的服务范围在 ASP.NET Core 应用中具有明确的边界, 指的是针对每个 HTTP 请求的上下文, 也就是服务范围的生命周期与每个请求上下文绑定在一起. 如下图所示, ASP.NET Core 应用中用于提供服务实例的 IServiceProvider 对象分为两种类型, 一种是作为根容器并与应用具有相同生命周期的 IServiceProvider 对象, 另一个类则是根据请求及时创建和释放的 IServiceProvider 对象, 我们一般将它们分别称为 ApplicationServices 和 RequestServices.
在 ASP.NET Core 应用初始化过程 (即请求管道构建过程) 中使用的服务实例都是由 ApplicationServices 提供的. 在具体处理每个请求时, ASP.NET Core 框架会利用注册的一个中间件来针对当前请求创建一个代表服务范围的 IServiceScope 对象, 该服务范围提供的 RequestServices 用来提供当前请求处理过程中所需的服务实例. 一旦服务请求处理完成, IServiceScoped 对象代表的服务范围被终结, 在当前请求处理过程中的 Scoped 服务会变成垃圾对象并最终被 GC 回收. 对于实现了 IDisposable 或者 IAsyncDisposable 接口的 Scoped 或者 Transient 服务实例来说, 在变成垃圾对象之前, 它们的 Dispose 或者 DisposeAsync 方法会被调用.
[ASP.NET Core 3 框架揭秘] 依赖注入[1]: 控制反转
[ASP.NET Core 3 框架揭秘] 依赖注入[2]:IoC 模式
[ASP.NET Core 3 框架揭秘] 依赖注入[3]: 依赖注入模式
[ASP.NET Core 3 框架揭秘] 依赖注入[4]: 一个迷你版 DI 框架
[ASP.NET Core 3 框架揭秘] 依赖注入[5]: 利用容器提供服务
[ASP.NET Core 3 框架揭秘] 依赖注入[6]: 服务注册
[ASP.NET Core 3 框架揭秘] 依赖注入[7]: 服务消费
[ASP.NET Core 3 框架揭秘] 依赖注入[8]: 服务实例的生命周期
[ASP.NET Core 3 框架揭秘] 依赖注入[9]: 实现概述
[ASP.NET Core 3 框架揭秘] 依赖注入[10]: 与第三方依赖注入框架的适配
[ASP.NET Core 3 框架揭秘] 依赖注入[8]: 服务实例的生命周期
来源: http://www.bubuko.com/infodetail-3297867.html