ServiceProvider实现

ServiceProvider实现揭秘 【总体设计 】

本系列前面的文章我们主要以编程的角度对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实现揭秘【补充漏掉的细节】

时间: 2024-11-19 14:42:53

ServiceProvider实现的相关文章

从两个不同的ServiceProvider说起

从两个不同的ServiceProvider说起 我们一致在说 ASP.NET Core广泛地使用到了依赖注入,通过前面两个系列的介绍,相信读者朋友已经体会到了这一点.由于前面两章已经涵盖了依赖注入在管道构建过程中以及管道在处理请求过程的应用,但是内容相对分散和零碎,我们有必要针对这个主题作一个归纳性的介绍.采用依赖注入的服务均由某个ServiceProvider来提供,但是在ASP.NET Core管道涉及到两个不同的ServiceProvider,其中一个是在管道成功构建后创建并绑定到WebH

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

通过上一篇的介绍我们应该对实现在ServiceProvider的总体设计有了一个大致的了解,但是我们刻意回避一个重要的话题,即服务实例最终究竟是采用何种方式提供出来的.ServiceProvider最终采用何种方式提供我们所需的服务实例取决于最终选择了怎样的ServiceCallSite,而服务注册是采用的ServiceDescriptor有决定了ServiceCallSite类型的选择.我们将众多不同类型的ServiceCallSite大体分成两组,一组用来创建最终的服务实例,另一类则与生命周

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

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

laravel中的Contracts, ServiceContainer, ServiceProvider, Facades关系

Contracts, ServiceContainer, ServiceProvider, Facades  Contracts 合同,契约,也就是接口,定义一些规则,每个实现此接口的都要实现里面的方法 ServiceContainer  实现Contracts,具体的逻辑实现 ServiceProvider    ServiceContainer的服务提供者,返回ServiceContainer的实例化,供其他地方使用,可以把它加入到app/config的provider中,会被自动注册到容器

通过重建Hosting系统理解HTTP请求在ASP.NET Core管道中的处理流程[下]:管道是如何构建起来的?

在<中篇>中,我们对管道的构成以及它对请求的处理流程进行了详细介绍,接下来我们需要了解的是这样一个管道是如何被构建起来的.总的来说,管道由一个服务器和一个HttpApplication构成,前者负责监听请求并将接收的请求传递给给HttpApplication对象处理,后者则将请求处理任务委托给注册的中间件来完成.中间件的注册是通过ApplicationBuilder对象来完成的,所以我们先来了解一下这究竟是个怎样的对象.[本文已经同步到<ASP.NET Core框架揭秘>之中] [

如何扩展 Visual Studio 编辑器

在 Visual Studio 2010 的时代,扩展 Visual Studio 的途径有很多,开发者可以选择宏.Add-in.MEF 和 VSPackages 进行自定义的扩展.但是宏在 Visual Studio 2012 的时候被阉割了,Add-in 也在 Visual Studio 2013 里被抹杀了,这样的调整对于 Visual Studio 来说是好的,但是对于那些习惯了使用宏和Add-in的团队可能就郁闷了. 本文将一步步教你如何实现对 Visual Studio 代码编辑器的

.Net Core(三)MVC Core

MVC Core的改动感觉挺大的,需要的功能大多从Nuget安装,还内置了IOC,支持SelfHost方式运行等等. 一.项目结构的变化创建的新MVC项目的结构发生了变化,比如:静态文件需要统一放置到wwwroot文件夹,配置文件变成了appsettings.json,用startup.cs文件代替了global文件:原先对view命名空间的引用配置在view下的web.config,现在则改成了使用_ViewImport.cshtml文件,并且用razor语法来配置. 二.内置IOCa)Aut

Asp.Net.Core 系列-中间件和依赖注入进阶篇

上一节讲了中间件和依赖注入的基础,紧接着: 中间件是怎么使用的?使用步骤是什么? 只要把中间件注册到管道中就行了,可以借助Startup对象(DelegateStartup或者ConventionBasedStartup)来完成之外,也可以利用另一个叫做StartupFilter的对象来实现.所谓的StartupFilter是对所有实现了IStartupFilter接口的类型及其对象的统称.IStartupFilter接口定义了如下一个唯一的方法Configure,该方法的参数next返回的Ac

Caliburn.Micro学习笔记(四)----IHandle&lt;T&gt;实现多语言功能

Caliburn.Micro学习笔记(四)----IHandle<T>实现多语言功能 说一下IHandle<T>实现多语言功能 因为Caliburn.Micro是基于MvvM的UI与codebehind分离, binding可以是双向的所以我们想动态的实现多语言切换很是方便今天我做一个小demo给大家提供一个思路 先看一下效果 点击英文  变成英文状态点chinese就会变成中文                          源码的下载地址在文章的最下边 多语言用的是资源文件建