Net6 DI源码分析Part5 在Kestrel内Di Scope生命周期是如何根据请求走的?
Net6 DI源码分析Part5 在Kestrel内Di Scope生命周期是如何根据请求走的?
在asp.net core中的DI生命周期有一个Scoped是根据请求走的,也就是说在处理一次请求时,Scope生命周期所提供的服务是同一个实例。它是用IServiceScope是实现的。但是我们要知道何时构建的IServiceScope以及IServiceScope何时被销毁掉
先说结论IServiceScope是根据当前的RequestServicesFeature内作为_scope成员存在的,只要知道RequestServicesFeature何时创建,何时销毁就了解整http request DI的生命周期
netcore中DI生命周期。
在net core 默认的DI中你直接build 出来的servicepProvider 用它去获取的实例对象Scoped & Singleton 是具有相同生命周期的。
如果需要有Scoped生命周期的实例,你需要通过serviceProvider创建一个IServiceScope实例,然后通过该实例获取到的服务实例会跟着IServiceScope一同销毁从而达到Scoped生命周期。
DI生命周期案例 1
ServiceCollection serviceCollection = new ServiceCollection();
serviceCollection.AddSingleton<MySingletonClass>();
serviceCollection.AddScoped<MyScopedClass>();
serviceCollection.AddTransient<MyTransientClass >();
var servicepProvider = serviceCollection.BuildServiceProvider();
using (IServiceScope scope = servicepProvider.CreateScope(), scope2 = servicepProvider.CreateScope())
{
var scopeObj1 = scope.ServiceProvider.GetService<MyScopedClass>();
var scopeObj2 = scope2.ServiceProvider.GetService<MyScopedClass>();
Console.WriteLine(object.ReferenceEquals(scopeObj1, scopeObj2)); //false
}
//Scoped Constructor
//Scoped Constructor
//False
//Scoped Dispose
//Scoped Dispose
Console.ReadLine();
RequestServicesFeature
为什么说http request DI 的生命周期是根据RequestServicesFeature来的。通过DI生命周期案例了解到需要创建一个scope生命周期,要有一个IServiceScope实例。
那么在http feature中RequestServicesFeature做了对该接口的封装。也用于提供RequestSerivces服务。
- IServiceScope的创建: HttpContext获取RequstServiceProivder是根据RequestServicesFeature.RequestServices。在RequestServices属性的get方法内创建_scope。并返回给该scope对应的ServiceProvide供后续使用。
- IServiceScope的销毁:在RequestServicesFeature.Dispose 方法内又调用了_scope属性(IServiceScope)的同名方法从而进行销毁由此提供的所有service。那么只要销毁了当前请求的RequestServicesFeature 实例就销毁了。当前http requst 的scope生命周期的所有服务。
RequestServicesFeature 是何时被销毁的。
- HttpProtocol.ProcessRequests 方法内调用await FireOnCompleted();
- FireOnCompleted内部会循环执行
Stack<KeyValuePair<Func<object, Task>, object>>? _onCompleted;堆栈委托 - requestServiceFeature.Dispose在此刻被调用,其内部Dispose了IServiceScope。自此ServiceScope生命周期结束。
RequestServicesFeature 简化代码
public class RequestServicesFeature : IServiceProvidersFeature, IDisposable, IAsyncDisposable
{
private IServiceScope? _scope;
private readonly HttpContext _context;
public RequestServicesFeature(HttpContext context, IServiceScopeFactory? scopeFactory)
{
_context = context;
_scopeFactory = scopeFactory;
}
public IServiceProvider RequestServices
{
get
{
_context.Response.RegisterForDisposeAsync(this);
_scope = _scopeFactory.CreateScope();
_requestServices = _scope.ServiceProvider;
return _requestServices!;
}
}
/// <inheritdoc />
public ValueTask DisposeAsync()
{
switch (_scope)
{
case IAsyncDisposable asyncDisposable:
var vt = asyncDisposable.DisposeAsync();
if (!vt.IsCompletedSuccessfully)
{
return Awaited(this, vt);
}
vt.GetAwaiter().GetResult();
break;
case IDisposable disposable:
disposable.Dispose();
break;
}
}
}
HttpProtocol
该类是处理http协议的 ProcessRequests作为重要方法之一,用来使用我们构建好的IHttpApplication 处理request
httpontext的创建,以及我们编排好的http中间件管道都是在这里被执行的。同时调用FireOnCompleted, RequestServicesFeature就是在这里被销毁的。
private async Task ProcessRequests<TContext>(IHttpApplication<TContext> application) where TContext : notnull
{
while (_keepAlive)
{
var context = application.CreateContext(this);
// Run the application code for this request
await application.ProcessRequestAsync(context);
if (_onCompleted?.Count > 0)
{
await FireOnCompleted();
}
application.DisposeContext(context, _applicationException);
}
}
protected Task FireOnCompleted()
{
var onCompleted = _onCompleted;
if (onCompleted?.Count > 0)
{
return ProcessEvents(this, onCompleted);
}
return Task.CompletedTask;
static async Task ProcessEvents(HttpProtocol protocol, Stack<KeyValuePair<Func<object, Task>, object>> events)
{
while (events.TryPop(out var entry))
{
try
{
await entry.Key.Invoke(entry.Value);
}
catch (Exception ex)
{
protocol.Log.ApplicationError(protocol.ConnectionId, protocol.TraceIdentifier, ex);
}
}
}
}
RequestServicesFeature 是如何注册到HttpProtocol _onCompleted;堆栈委托中的
HttpProtocol委托堆栈中 RequestServicesFeature 是什么时候被注册进去的呢?
- RequestServicesFeature.RequestServices属性的Get方法调用了方法内有一句这样的代码
_context.Response.RegisterForDisposeAsync(this); - HttpResponse的RegisterForDisposeAsync调用了抽象方法abstract void OnCompleted。实现该方法的是在完成的DefaultHttpResponse
- DefaultHttpResponse的OnCompleted把委托注册到HttpProtocol.OnCompleted方法注册到_onCompleted中,实际代码体现为
HttpResponseFeature.OnCompleted(callback, state);这里HttpResponseFeature.就是就是HttpProtocol,(HttpProtoccol是个IFeatureCollection接口的实现。)
Net6 DI源码分析Part5 在Kestrel内Di Scope生命周期是如何根据请求走的?的更多相关文章
- MyBatis源码分析(5)——内置DataSource实现
@(MyBatis)[DataSource] MyBatis源码分析(5)--内置DataSource实现 MyBatis内置了两个DataSource的实现:UnpooledDataSource,该 ...
- vue 源码详解(二): 组件生命周期初始化、事件系统初始化
vue 源码详解(二): 组件生命周期初始化.事件系统初始化 上一篇文章 生成 Vue 实例前的准备工作 讲解了实例化前的准备工作, 接下来我们继续看, 我们调用 new Vue() 的时候, 其内部 ...
- Net6 DI源码分析Part4 CallSiteFactory ServiceCallSite
Net6 CallSiteFactory ServiceCallSite, CallSiteChain abstract class ServiceCallSite ServiceCallSite是个 ...
- 小白都能看懂的 Spring 源码揭秘之依赖注入(DI)源码分析
目录 前言 依赖注入的入口方法 依赖注入流程分析 AbstractBeanFactory#getBean AbstractBeanFactory#doGetBean AbstractAutowireC ...
- Handle源码分析,深入群内了解风骚的Handle机制
Hanlder的使用方式一: private static Handler mHandler = new Handler(){ public void handleMessage(android.os ...
- Springboot源码分析之代理对象内嵌调用
摘要: 关于这个话题可能最多的是@Async和@Transactional一起混用,我先解释一下什么是代理对象内嵌调用,指的是一个代理方法调用了同类的另一个代理方法.首先在这儿我要声明事务直接的嵌套调 ...
- Spark Streaming源码解读之流数据不断接收全生命周期彻底研究和思考
本期内容 : 数据接收架构设计模式 数据接收源码彻底研究 一.Spark Streaming数据接收设计模式 Spark Streaming接收数据也相似MVC架构: 1. Mode相当于Rece ...
- Net6 DI源码分析Part2 Engine,ServiceProvider
ServiceProvider ServiceProvider是对IServiceProvider实现,它有一个internal的访问修饰符描述的构造,并需要两个参数IServiceCollectio ...
- Net6 DI源码分析Part1 ServiceCollection、ServiceDescriptor、ServiceLifetime、IServiceProvider
ServiceCollection.ServiceDescriptor.ServiceLifetime.IServiceProvider Microsoft.Extensions.Dependency ...
随机推荐
- Java GUI 桌面应用开发
前言 虽然现在已经很少项目会涉及GUI技术,但作为一个合格的Java开发工程师,还是得了解才得 本文记录,idea使用JFormDesigner插件进行Java GUI 桌面应用开发 GUI Swin ...
- Spring Boot 2 中如何使用 Log4j2 记录日志
上一篇我们介绍了Spring Boot 2.x中默认日志框架Logback的使用.今天继续说说日志,接下来我们要讲是前段时间爆出核弹漏洞的Log4j2.虽然出了漏洞,让很多小伙伴痛苦了1-2周(加班) ...
- CapstoneCS5212|DP to VGA|CS5212设计电路方案
CS5212功能概述 CS5212是一款DisplayPort端口到VGA转换器,它结合了DisplayPort输入接口和模拟RGB DAC输出接口.嵌入式单片机基于工业标准8051核心. CS521 ...
- 网络划分和各层协议以及webservice 浅谈
最近在公司做一些和其他外部系统接口调用的工作,遇到一些网络传输的问题,趁周末的时间记录.整理一下. 提到网络我们不得不提网络的分层架构: 我们通常听到 网络七层架构/五层架构/四层架构,但是不了解很容 ...
- 基于Spring MVC + Spring + MyBatis的【超市会员管理系统】
资源下载: https://download.csdn.net/download/weixin_44893902/22035329 一. 语言和环境 实现语言:JAVA语言. 使用:MyEclipse ...
- Log4j2基本使用入门
1.Log4j2简介 Apache Log4j 2是日志框架Log4j的升级, 它比其前身Log4j 1.x提供了重要的改进, 并且参考了Logback中许多有用的改进, 同时修复了Logback的一 ...
- Red Scarf abc171_E
题目大意 除去一个元素后的异或和 题目分析 首先异或有交换律 \(a \ \mathrm{xor} \ a=0\) \(0 \ \mathrm{xor} \ a=a\) 可以求出异或和,在异或当前元素 ...
- Oracle 查询NULL字段/空字符串
简单记录一下: 工作中需要查询某个字段值为空的数据, 最开始查询的时候按照以前的思路 : 1.where 字段名=NULL,点击F8,未查到结果: 2.where 字段名='',点击F8,未查到结果: ...
- 如何在 CentOS 上安装 dos2unix 和 unix2dos 命令
yum install -y dos2unix 注意:以上安装包既包含 dos2unix 命令,又包含 unix2dos 命令.
- Linux上天之路(三)之Linux系统目录
1. Linux设计思想 1) 程序应该小而专一,程序应该尽量的小,且只专注于一件事上,不要开发那些看起来有用但是90%的情况都用不到的特性: 2) 程序不只要考虑性能, 程序的可移植性更重要,she ...