一:背景

1. 讲故事

前几天有位朋友让我有时间分析一下 aspnetcore 中为什么向 ServiceCollection 中注入的 Class 可以做到 Singleton,Transient,Scoped,挺有意思,这篇就来聊一聊这一话题,自从 core 中有了 ServiceCollection, 再加上流行的 DDD 模式,相信很多朋友的项目中很少能看到 new 了,好歹 spring 十几年前就是这么干的。

二:Singleton,Transient,Scoped 基本用法

分析源码之前,我觉得有必要先介绍一下它们的玩法,为方便演示,我这里就新建一个 webapi 项目,定义一个 interface 和 concrete ,代码如下:

    public class OrderService : IOrderService
{
private string guid; public OrderService()
{
guid = $"时间:{DateTime.Now}, guid={ Guid.NewGuid()}";
} public override string ToString()
{
return guid;
}
} public interface IOrderService
{
}

1. AddSingleton

正如名字所示它可以在你的进程中保持着一个实例,也就是说仅有一次实例化,不信的话代码演示一下哈。


public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(); services.AddSingleton<IOrderService, OrderService>();
}
} [ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
IOrderService orderService1;
IOrderService orderService2; public WeatherForecastController(IOrderService orderService1, IOrderService orderService2)
{
this.orderService1 = orderService1;
this.orderService2 = orderService2;
} [HttpGet]
public string Get()
{
Debug.WriteLine($"{this.orderService1}\r\n{this.orderService2} \r\n ------");
return "helloworld";
}
}

接着运行起来多次刷新页面,如下图:

可以看到,不管你怎么刷新页面,guid都是一样,说明确实是单例的。

2. AddScoped

正从名字所述:Scope 就是一个作用域,那在 webapi 或者 mvc 中作用域是多大呢? 对的,就是一个请求,当然请求会穿透 Presentation, Application, Repository 等等各层,在穿层的过程中肯定会有同一个类的多次注入,那这些多次注入在这个作用域下维持的就是单例,如下代码所示:


public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(); services.AddScoped<IOrderService, OrderService>();
}

运行起来多次刷新页面,如下图:

很明显的看到,每次刷 UI 的时候,guid都会变,而在同一个请求 (scope) 中 guid 是一样的。

3. AddTransient

前面大家也看到了,要么作用域是整个进程,要么作用域是一个请求,而这里的 Transient 就没有作用域概念了,注入一次 实例化一次,不信的话上代码给你看呗。


public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(); services.AddTransient<IOrderService, OrderService>();
}

从图中可以看到,注入一次就 new 一次,非常简单吧,当然了,各有各的应用场景。

之前不清楚的朋友到现在应该也明白了这三种作用域,接下来继续思考的一个问题就是,这种作用域是如何做到的呢? 要想回答这个问题,只能研究源代码了。

三:源码分析

aspnetcore 中的 IOC 容器是 ServiceCollection,你可以向 IOC 中注入不同作用域的类,最后生成 provider,如下代码所示:


var services = new ServiceCollection(); services.AddSingleton<IOrderService, OrderService>(); var provider = services.BuildServiceProvider();

1. AddSingleton 的作用域是如何实现的

通常说到单例,大家第一反应就是 static,但是一般 ServiceCollection 中会有成百上千个 AddSingleton 类型,都是静态变量是不可能的,既然不是 static,那就应该有一个缓存字典什么的,其实还真的有这么一个。

1)RealizedServices 字典

每一个 provider 内部都会有一个 叫做 RealizedServices 的字典,这个 字典 将会在后面充当缓存存在, 如下图:

从上图中可以看到,初始化的时候这个字典什么都没有,接下来执行 var orderService = provider.GetService<IOrderService>(); 效果如下图:

可以看到 RealizedServices 中已经有了一个 service 记录了,接着往下执行 var orderService2 = provider.GetService<IOrderService>();,最终会进入到 CallSiteRuntimeResolver.VisitCache 方法判断实例是否存在,如下图:

仔细看上面代码的这句话: if (!resolvedServices.TryGetValue(callSite.Cache.Key, out obj)) 一旦字典存在就直接返回,否则就要执行 new 链路,也就是 this.VisitCallSiteMain

综合来看,这就是为什么可以单例的原因,如果不明白可以拿 dnspy 仔细琢磨琢磨。。。

2. AddTransient 源码探究

前面大家也看到了,provider 里面会有一个 DynamicServiceProviderEngine 引擎类,引擎类中用 字典缓存 来解决单例问题,可想而知,AddTransient 内部肯定是没有字典逻辑的,到底是不是呢? 调试一下呗。

和单例一样,最终解析都是由 CallSiteRuntimeResolver 负责的,AddTransient 内部会走到 VisitDisposeCache 方法,而这里会一直走 this.VisitCallSiteMain(transientCallSite, context) 来进行 实例的 new 操作,还记得单例是怎么做的吗? 它会在这个 VisitCallSiteMain 上包一层 resolvedServices 判断,

终于弄明白了 Singleton,Transient,Scoped 的作用域是如何实现的的更多相关文章

  1. 关于java中是引用传递还是值传递的问题!!!经常在笔试中遇到,今天终于弄明白了!

    关于JAVA中参数传递问题有两种,一种是按值传递(如果是基本类型),另一种是按引用传递(如果是對象).首先以两个例子开始:1)public class Test2 { public static vo ...

  2. PID算法终于弄明白原理了,原来就这么简单

    看起来PID高大尚,实则我们都是被他的外表所震撼住了.先被别人唬住,后被公式唬住,由于大多数人高数一点都不会或者遗忘,所以再一看公式,简直吓死.了解了很浅的原理后,结果公式看不懂,不懂含义,所以最终没 ...

  3. 深入理解net core中的依赖注入、Singleton、Scoped、Transient(一)

    相关文章: 深入理解net core中的依赖注入.Singleton.Scoped.Transient(一) 深入理解net core中的依赖注入.Singleton.Scoped.Transient ...

  4. 深入理解net core中的依赖注入、Singleton、Scoped、Transient(三)

    相关文章: 深入理解net core中的依赖注入.Singleton.Scoped.Transient(一) 深入理解net core中的依赖注入.Singleton.Scoped.Transient ...

  5. .net core 注入中的三种模式:Singleton、Scoped 和 Transient

    从上篇内容不如题的文章<.net core 并发下的线程安全问题>扩展认识.net core注入中的三种模式:Singleton.Scoped 和 Transient 我们都知道在 Sta ...

  6. 深入理解net core中的依赖注入、Singleton、Scoped、Transient(四)

    相关文章: 深入理解net core中的依赖注入.Singleton.Scoped.Transient(一) 深入理解net core中的依赖注入.Singleton.Scoped.Transient ...

  7. 深入理解net core中的依赖注入、Singleton、Scoped、Transient(二)

    相关文章: 深入理解net core中的依赖注入.Singleton.Scoped.Transient(一) 深入理解net core中的依赖注入.Singleton.Scoped.Transient ...

  8. 深入理解net core中的依赖注入、Singleton、Scoped、Transient(四)【转】

    原文链接:https://www.cnblogs.com/gdsblog/p/8465401.html 相关文章: 深入理解net core中的依赖注入.Singleton.Scoped.Transi ...

  9. 几张图弄明白ios布局中的尺寸问题

    背景 先说说逆向那事.各种曲折..各种技术过时,老老实实在啃看雪的帖子..更新会有的. 回正题,这里讨论的是在Masnory框架下的布局问题.像我这种游击队没师傅带,什么都得自己琢磨,一直没闹明白下面 ...

随机推荐

  1. bzoj 1515 [POI2006]Lis-The Postman 有向图欧拉回路

    LINK:Lis-The Postman 看完题觉得 虽然容易发现是有向图欧拉回路 但是觉得很难解决这个问题. 先分析一下有向图的欧拉回路:充要条件 图中每个点的入度-出度=0且整张图是一个强连通分量 ...

  2. 牛客练习赛63 牛牛的斐波那契字符串 矩阵乘法 KMP

    LINK:牛牛的斐波那契字符串 虽然sb的事实没有改变 但是 也不会改变. 赛时 看了E和F题 都不咋会写 所以弃疗了. 中午又看了一遍F 发现很水 差分了一下就过了. 这是下午和古队长讨论+看题解的 ...

  3. PHP爬取网页的主要方法,你掌握了吗

    这篇文章讲的是PHP爬取网页的主要方法,主要流程就是获取整个网页,然后正则匹配(关键的). PHP抓取页面的主要方法,有几种方法是网上前辈的经验,现在还没有用到的,先存下来以后试试. file()函数 ...

  4. Python 写一个俄罗斯方块游戏

    使用 Python 的 PyGame 库写一个俄罗斯方块游戏的逐步指南 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手.很多已经做案例的人 ...

  5. EasyExcel的基本使用方法

    在Java语言领域,说到Excel处理工具,大家首先想到的可能是阿帕奇的poi,poi在处理数据量不大的excel文件上确实非常强大,但是随着后来excel从03(一个excel文件中最多有65536 ...

  6. 2020-08-02:输入ping IP 后敲回车,发包前会发生什么?

    福哥答案2020-08-02: 首先根据目的IP和路由表决定走哪个网卡,再根据网卡的子网掩码地址判断目的IP是否在子网内.如果不在则会通过arp缓存查询IP的网卡地址,不存在的话会通过广播询问目的IP ...

  7. Windows下,配置VS Code的Java开发环境

    Windows下,配置VS Code的Java开发环境 前言 最近痴迷于VS Code的开发环境配置,原因就在于它的轻巧和免费,还能当一个非常棒的文本编辑器.如果之前你配置过VS Code并且失败了, ...

  8. windows下的Redis安装:

    windows下的Redis安装: 百度网盘地址:https://pan.baidu.com/s/1yYED2pXLWolPXvWaABtF2Q 提取密码:xshu 1.解压文件并且创建start.b ...

  9. 【UVa1635】Irrelevant Elements - 唯一分解定理

    题意 给你 \(n\) 个数,每次求出相邻两个数的和组成新数列.经过 \(n-1\) 次操作后,得到一个数.求这个数 \(mod \ m\) 与哪些项无关. 如:当 \(m=2 \ , \ n=2\) ...

  10. docker入门4-docker stack

    stack介绍 stack是一组共享依赖,可以被编排并具备扩展能力的关联service.举例来说就是在swarm那章描述docker层次架构时,说stack就是一个完整的服务--它可以由基于flask ...