本系列前面的文章我们主要以编程的角度对ASP.NET Core的依赖注入系统进行了详细的介绍,如果读者朋友们对这些内容具有深刻的理解,我相信你们已经可以正确是使用这些与依赖注入相关的API了。如果你还对这个依赖注入系统底层的实现原理具有好奇心,可以继续阅读这一节的内容。

目录
一、ServiceCallSite

二、Service

三、ServiceEntry

四、ServiceTable

五、ServiceProvider

作为DI容器的体现,ServiceProvider是ASP.NET Core依赖注入系统的一个核心对象,但是默认的实现者是一个定义在程序集 “Microsoft.Extensions.DependencyInjection.dll” 中的一个名为 “ServiceProvider” 内部(Internal)类型,而且它所依赖的很多接口和类型也是如此,所以我相信实现在这个ServiceProvider类中的服务提供机制对于绝大部分人是陌生的。本节提及的ServiceProvider不是泛指实现了IServiceProvider接口的类型,而是专指ServiceProvider这个内部类型。

为了让读者朋友们能够深刻地了解ServiceProvider内部的实现原理,我会在本节内容中重新定义它。在这里需要特别说明的是我们重建的ServiceProvider以及其他重建的接口和类旨在体现真实ServiceProvider设计思想和实现原理,在具体的源代码层面是有差异的。考虑到篇幅的问题,很多细节的内容将不会体现在我们重建的接口和类型中。如果想了解原始的实现逻辑,可以从GitHub上下载源代码。

从总体设计的角度来审视ServiceProvider,需要涉及与之相关的4个核心对象,包括ServiceCallSite、Service、ServiceEntry和ServiceTable,它们均体现为相应的接口和类,并且这些接口和泪都是内部的,接下来我们就来逐一认识它们。

一、ServiceCallSite

ServiceProvider的核心功能就是针对服务类型提供相应的服务实例,而服务实例的提供最终是通过ServiceCallSite来完成的。ServiceCallSite体现为具有如下定义的IServiceCallSite接口,除了直接提供服务实例的Invoke方法之外,它还具有另一个返回类型为Expression的Build方法,该方法将定义在Invoke方法中的逻辑定义成一个表达式。

   1: internal interface IServiceCallSite

   2: {

   3:     object Invoke(ServiceProvider provider);

   4:     Expression Build(Expression provider);

   5: }

在真正提供服务实例的时候,ServiceProvider在收到针对某个服务类型的第一个服务获取请求时,他会直接调用对应ServiceCallSite的Invoke方法返回提供的服务实例。与此同时,这个ServiceCallSite的Build方法会被调用并生成一个表达式,该表达式进一步编译成一个类型为Func<ServiceProvider, object>的委托对象并被缓存起来。针对同一个服务类型的后续服务实例将直接使用这个缓存的委托对象来提供。

二、Service

我们知道ServiceProvider提供服务的依据来源于创建它指定一个ServiceCollection对象,用于指导ServiceProvider如何提供所需服务的信息以ServiceDescriptor对象的形式保存在这个集合对象中。当ServiceProvider被初始化后,每一个ServiceDescriptor将会被转换成一个Service对象,后者体现为如下一个IService接口。

   1: internal interface IService

   2: {

   3:     IService Next { get; set; }

   4:     ServiceLifetime Lifetime { get; }

   5:     IServiceCallSite CreateCallSite(ServiceProvider provider, ISet<Type> callSiteChain);

   6: }

Service的Lifetime属性自然来源于ServiceDescriptor的同名属性,它的CreateCallSite方法返回一个针对用于提供对应服务实例的ServiceCallSite对象。由于Service对象可以创建ServiceCallSite,所以它自然具有提供服务实例的能力。Service总是作为链表的某个节点存在,这个链表是具有相同服务类型(对应ServiceType属性)的多个ServiceDescriptot生成的,Service的Next属性保持着对链表后一个节点的引用。

三、ServiceEntry

上面我们所说的由Service对象组成的链表体现为如下一个ServiceEntry类。我们为ServiceEntry定义了三个属性(First、Last、All)分别代笔这个链表的第一个节点、最后一个节点以及所有节点,节点类型为IService。如果需要在链尾追加一个Service对象,可以直接调用Add方法。

   1: internal class ServiceEntry

   2: {

   3:     public IService         First { get; private set; }

   4:     public IService         Last { get; private set; }

   5:     public IList<IService>     All { get; private set; } = new List<IService>();

   6:  

   7:     public ServiceEntry(IService service)

   8:     {

   9:         this.First = service;

  10:         this.Last = service;

  11:         this.All.Add(service);

  12:     }

  13:  

  14:     public void Add(IService service)

  15:     {

  16:         this.Last.Next = service;

  17:         this.Last = service;

  18:         this.Add(service);

  19:     }

  20: }

四、ServiceTable

多个ServiceEntry组成一个ServiceTable。如下面的代码片段所示,一个ServiceTable通过其只读属性ServieEntries维护着一组ServiceEntry对象与它们对应的服务类型之间的映射关系。一个ServiceTable对象通过一个ServiceCollection对象创建出来。如下面的代码片段所示,组成ServiceCollection的所有ServiceDescriptor对象先根据其ServiceType属性体现的服务类型进行分组,由每组ServiceDescriptor创建的ServiceEntry对象与对应的服务类型之间的映射会被添加到ServiceEntries属性中。

   1: internal class ServiceTable

   2: {

   3:     public IDictionary<Type, ServiceEntry> ServieEntries { get; private set; } = new Dictionary<Type, ServiceEntry>();

   4:  

   5:     public ServiceTable(IServiceCollection services)

   6:     {

   7:         foreach (var group in services.GroupBy(it=>it.ServiceType))

   8:         {

   9:             ServiceDescriptor[] descriptors = group.ToArray();

  10:             ServiceEntry entry = new ServiceEntry(new Service(descriptors[0]));

  11:             for (int index = 1; index < descriptors.Length; index++)

  12:             {

  13:                 entry.Add(new Service(descriptors[index]));

  14:             }

  15:             this.ServieEntries[group.Key] = entry;

  16:         }

  17:         //省略其他代码

  18:     }

  19: }

从上面的代码片段可以看出组成ServiceEntry的是一个类型为Service的对象,该类型定义如下。Service类实现了IService接口并通过一个ServiceDescriptor对象创建而成。我们省略了定义在方法CreateCallSite中创建ServiceCallSite的逻辑,后续在介绍各种类型的ServiceCallSite的时候我们会回来讲述该方法的实现。

   1: internal class Service : IService

   2: {

   3:     public ServiceDescriptor     ServiceDescriptor { get; private set; }

   4:     public ServiceLifetime         Lifetime => this.ServiceDescriptor.Lifetime;

   5:     public IService             Next { get; set; }

   6:  

   7:     public Service(ServiceDescriptor serviceDescriptor)

   8:     {

   9:         this.ServiceDescriptor = serviceDescriptor;

  10:     }

  11:  

  12:     public IServiceCallSite CreateCallSite(ServiceProvider provider, ISet<Type> callSiteChain)

  13:     {

  14:         <<省略实现>>

  15:     }

  16: }

五、ServiceProvider

如下所示的代码片段揭示了实现在ServiceProvider之中与服务提供和回收相关的基本实现原理。我们先来简单介绍定义在它内部的几个属性。Root属性返回的ServiceProvider代表它的根,对于一个独立的ServiceProvider来说,这个根就是它自己。ServiceTable属性返回根据ServiceCollection创建的ServiceTable对象。上面介绍ServiceCallSite的时候,我们提到它的Build方法返回的表达式会编译成一个类型为Func <ServiceProvider,object>的委托,并被缓存起来服务于后续针对同一个类型的服务提供请求,该委托对象与对应服务类型之间的映射关系就保存在RealizedServices属性中。

   1: internal class ServiceProvider : IServiceProvider, IDisposable

   2: {

   3:     public ServiceProvider Root { get; private set; }

   4:     public ServiceTable ServiceTable { get; private set; }

   5:     public ConcurrentDictionary<Type, Func<ServiceProvider, object>> RealizedServices { get; private set; } = new ConcurrentDictionary<Type, Func<ServiceProvider, object>>();

   6:     public IList<IDisposable> TransientDisposableServices { get; private set; } = new List<IDisposable>();

   7:     public ConcurrentDictionary<IService, object> ResolvedServices { get; private set; } = new ConcurrentDictionary<IService, object>();

   8:     

   9:     public ServiceProvider(IServiceCollection services)

  10:     {

  11:         this.Root         = this;

  12:         this.ServiceTable     = new ServiceTable(services);

  13:     }

  14:  

  15:     public object GetService(Type serviceType)

  16:     {

  17:         Func<ServiceProvider, object> serviceAccessor;

  18:         if (this.RealizedServices.TryGetValue(serviceType, out serviceAccessor))

  19:         {

  20:             return serviceAccessor(this);

  21:         }

  22:  

  23:         IServiceCallSite serviceCallSite = this.GetServiceCallSite(serviceType, new HashSet<Type>());

  24:         if (null != serviceCallSite)

  25:         {

  26:             var providerExpression = Expression.Parameter(typeof(ServiceProvider), "provider");

  27:             this.RealizedServices[serviceType] = Expression.Lambda<Func<ServiceProvider, object>>(serviceCallSite.Build(providerExpression), providerExpression).Compile();

  28:             return serviceCallSite.Invoke(this);

  29:         }

  30:  

  31:         this.RealizedServices[serviceType] = _ => null;

  32:         return null;

  33:     }

  34:  

  35:     public IServiceCallSite GetServiceCallSite(Type serviceType, ISet<Type> callSiteChain)

  36:     {

  37:             try

  38:             {

  39:                 if (callSiteChain.Contains(serviceType))

  40:                 {

  41:                     throw new InvalidOperationException(string.Format("A circular dependency was detected for the service of type '{0}'", serviceType.FullName);

  42:                 }

  43:                 callSiteChain.Add(serviceType);

  44:  

  45:                 ServiceEntry serviceEntry;

  46:                 if (this.ServiceTable.ServieEntries.TryGetValue(serviceType, 

  47:                     out serviceEntry))

  48:                 {

  49:                     return serviceEntry.Last.CreateCallSite(this, callSiteChain);

  50:                 }

  51:  

  52:                 //省略其他代码

  53:  

  54:                 return null;

  55:             }

  56:             finally

  57:             {

  58:                 callSiteChain.Remove(serviceType);

  59:             }

  60:     }    

  61:  

  62:     public void Dispose()

  63:     {

  64:         Array.ForEach(this.TransientDisposableServices.ToArray(), _ => _.Dispose());

  65:         Array.ForEach(this.ResolvedServices.Values.ToArray(), _ => (_ as IDisposable)?.Dispose());

  66:         this.TransientDisposableServices.Clear();

  67:         this.ResolvedServices.Clear();

  68:     }

  69:     //其他成员

  70: }

对于采用Scoped模式提供的服务实例,ServiceProvider需要自行对它们进行维护,具体来说它们会和对应的Service对象之间的映射关系会保存在ResolvedServices属性中。如果采用Transient模式,对于提供过的服务实例,如果自身类型实现了IDisposble接口,它们会被添加到TransientDisposableServices属性返回的列表中。当Dispose方法执行的时候,这两组对象的Dispose方法会被执行。

真正的服务提供机制体现在ServiceProvider实现的GetService方法中,实现逻辑其实很简单:ServiceProvider会根据指定的服务类型从RealizedServices属性中查找是否有通过编译表达式生成的Func<ServiceProvider,object>委托生成出来,如果存在则直接使用它生成提供的服务实例。如果这样的委托不存在,则会试着从ServiceTable中找到对应的ServiceEntry,如果不存在直接返回Null,否则会调用ServiceEntry所在列表最后一个Service的CreateServiceCallSite方法创建一个ServiceCallSite对象(这一点说明了如果针对同一个服务类型注册了多个ServiceDescriptor,在提供单个服务的时候总是使用最后一个ServiceDescriptor)。

接下来这个ServiceCallSite的Invoke方法被调用来创建服务实例,在返回该实例之前它的Build方法会被调用,返回的表达式被编译成Func<ServiceProvider,object>委托并被添加到RealizedServices属性中。如果ServiceProvider后续需要提供同类型的服务,这个委托对象将被启用。

ASP.NET Core中的依赖注入(1):控制反转(IoC)
ASP.NET Core中的依赖注入(2):依赖注入(DI)
ASP.NET Core中的依赖注入(3):服务注册与提取
ASP.NET Core中的依赖注入(4):构造函数的选择与生命周期管理
ASP.NET Core中的依赖注入(5):ServicePrvider实现揭秘【总体设计】
ASP.NET Core中的依赖注入(5):ServicePrvider实现揭秘【解读ServiceCallSite】
ASP.NET Core中的依赖注入(5):ServicePrvider实现揭秘【补充漏掉的细节】

ASP.NET Core中的依赖注入(5): ServiceProvider实现揭秘 【总体设计 】的更多相关文章

  1. ASP.NET Core中的依赖注入(1):控制反转(IoC)

    ASP.NET Core在启动以及后续针对每个请求的处理过程中的各个环节都需要相应的组件提供相应的服务,为了方便对这些组件进行定制,ASP.NET通过定义接口的方式对它们进行了"标准化&qu ...

  2. ASP.NET Core中的依赖注入(2):依赖注入(DI)

    IoC主要体现了这样一种设计思想:通过将一组通用流程的控制从应用转移到框架之中以实现对流程的复用,同时采用"好莱坞原则"是应用程序以被动的方式实现对流程的定制.我们可以采用若干设计 ...

  3. ASP.NET Core中的依赖注入(3): 服务的注册与提供

    在采用了依赖注入的应用中,我们总是直接利用DI容器直接获取所需的服务实例,换句话说,DI容器起到了一个服务提供者的角色,它能够根据我们提供的服务描述信息提供一个可用的服务对象.ASP.NET Core ...

  4. ASP.NET Core中的依赖注入(4): 构造函数的选择与服务生命周期管理

    ServiceProvider最终提供的服务实例都是根据对应的ServiceDescriptor创建的,对于一个具体的ServiceDescriptor对象来说,如果它的ImplementationI ...

  5. ASP.NET Core中的依赖注入(5): ServiceProvider实现揭秘 【解读ServiceCallSite 】

    通过上一篇的介绍我们应该对实现在ServiceProvider的总体设计有了一个大致的了解,但是我们刻意回避一个重要的话题,即服务实例最终究竟是采用何种方式提供出来的.ServiceProvider最 ...

  6. ASP.NET Core中的依赖注入(5):ServicePrvider实现揭秘【补充漏掉的细节】

    到目前为止,我们定义的ServiceProvider已经实现了基本的服务提供和回收功能,但是依然漏掉了一些必需的细节特性.这些特性包括如何针对IServiceProvider接口提供一个Service ...

  7. ASP.NET Core 中的依赖注入

    目录 什么是依赖注入 ASP .NET Core 中使用依赖注入 注册 使用 释放 替换为其它的 Ioc 容器 参考 什么是依赖注入 软件设计原则中有一个依赖倒置原则(DIP),为了更好的解耦,讲究要 ...

  8. ASP.NET Core 中的 依赖注入介绍

    ASP.NET Core 依赖注入 HomeController public class HomeController : Controller { private IStudentReposito ...

  9. ASP.NET Core 中的依赖注入 [共7篇]

    一.控制反转(IoC) ASP.NET Core在启动以及后续针对每个请求的处理过程中的各个环节都需要相应的组件提供相应的服务,为了方便对这些组件进行定制,ASP.NET通过定义接口的方式对它们进行了 ...

随机推荐

  1. Hive函数大全

    一.关系运算: 1. 等值比较: = 语法:A=B 操作类型:所有基本类型 描述: 如果表达式A与表达式B相等,则为TRUE:否则为FALSE 举例: hive> select 1 from l ...

  2. C#在Linux+Mono环境中使用微信支付证书

    最近特殊的需求,要把微信平台一个功能页面部署到Linux(CentOS6.5)下,其中涉及到微信支付退款. 鉴于之前实践过mono+jexus+asp.net mvc的部署,于是问题重点在于解决对商户 ...

  3. [RxJava^Android]项目经验分享 --- RxLifecycle功能实现分析(一)

      最近在研究RxJava自定义操作符的实现原理,发现成型的项目案例较少.突然想起在项目中应用的RxLifecycle是使用自定义操作符,便拿来研究了一下.分析之前,跟大家了解一些相关操作符和RxLi ...

  4. 整理react native的资料

    http://blog.csdn.net/jj120522/article/details/51900016  -----RN常见的错误 http://www.mamicode.com/info-de ...

  5. Linux Shell 重定向与管道【转帖】

    by 程默 在了解重定向之前,我们先来看看linux 的文件描述符. linux文件描述符:可以理解为linux跟踪打开文件,而分配的一个数字,这个数字有点类似c语言操作文件时候的句柄,通过句柄就可以 ...

  6. windows下python的tar.gz文件安装

    windows下下载了django,PIL,web.py发现都是tar.gz格式的文件,网上查找也非常系统的方法,总结一下其他大神的方法,归纳于此. 首先下载tar.gz文件,比如web.py,下载后 ...

  7. Ncut源码编译错误的解决方法

    NCut是一个比较老的开源代码了.所以在新的matlab的环境下老出各种bug. 经过自己的各种折腾,总结为一下几点: 1.保证matlab的mex是有C编译器可以用的,具体可以用 mex -setu ...

  8. springmvc上传文件,抄别人的

    SpringMVC中的文件上传 分类: SpringMVC 2012-05-17 12:55 26426人阅读 评论(13) 收藏 举报 stringuserinputclassencoding 这是 ...

  9. iOS. PercentEscape是错用的URLEncode,看看AFN和Facebook吧

    别再使用stringByAddingPercentEscapesUsingEncoding 当遇到发送网络请求的参数中有汉字的情况,很多人一股脑地使用stringByAddingPercentEsca ...

  10. Java ArrayList和Vector、LinkedList与ArrayList、数组(Array)和列表集合(ArrayList)的区别

    ArrayList和Vector的区别ArrayList与Vector主要从二方面来说.  一.同步性:   Vector是线程安全的,也就是说是同步的,而ArrayList是线程序不安全的,不是同步 ...