服务注册》、《服务消费》和《生命周期》主要从实现原理的角度对.NET Core的依赖注入框架进行了介绍,接下来更进一步,看看该框架的总体设计和实现。在过去的多个版本更迭过程中,依赖注入框架的底层实现一直都在发生改变,加上底层的涉及的大都是内容接口和类型,所以我们不打算涉及太过细节的层面。

一、ServiceProviderEngine & ServiceProviderEngineScope

对于依赖注入的底层设计和实现来说,ServiceProviderEngine和ServiceProviderEngineScope是两个最为核心的类型。顾名思义,ServiceProviderEngine表示提供服务实例的提供引擎,服务实例最终是通过该引擎提供的,在一个应用范围内只存在一个全局唯一的ServiceProviderEngine对象。ServiceProviderEngineScope代表服务范围,它利用对提供服务实例的缓存实现对生命周期的控制。ServiceProviderEngine实现了接口IServiceProviderEngine,从如下的代码片段可以看出,一个ServiceProviderEngine对象同时也是一个IServiceProvider对象,还是一个IServiceScopeFactory对象。

internal interface IServiceProviderEngine :  IServiceProvider, IDisposable, IAsyncDisposable {     void ValidateService(ServiceDescriptor descriptor);     IServiceScope RootScope { get; } }  internal abstract class ServiceProviderEngine : IServiceProviderEngine, IServiceScopeFactory {     public IServiceScope RootScope { get; }         public IServiceScope CreateScope();     ... }

ServiceProviderEngine的RootScope属性返回的IServiceScope对象是为根容器提供的服务范围。作为一个IServiceScopeFactory对象,ServiceProviderEngine的CreateScope会创建一个新的服务范围,这两种服务范围都通过一个ServiceProviderEngineScope对象来表示。

internal class ServiceProviderEngineScope : IServiceScope, IDisposable,  IServiceProvider, IAsyncDisposable {     public ServiceProviderEngine Engine { get; }     public IServiceProvider ServiceProvider { get; }     public object GetService(Type serviceType); }

如上面的代码片段所示,一个ServiceProviderEngineScope对象不仅是一个IServiceScope对象,还是一个IServiceProvider对象。在《生命周期》中,我们说表示服务范围的IServiceScope对象是对一个表示依赖注入容器的IServiceProvider对象的封装,实际上两者合并为同一个ServiceProviderEngineScope对象,一个ServiceProviderEngineScope对象的ServiceProvider属性返回的就是它自己。换句话说,我们所谓的子容器和它所在的服务范围引用的都是同一个ServiceProviderEngineScope对象。

4-12

下图进一步揭示了ServiceProviderEngine和ServiceProviderEngineScope之间的关系。对于一个通过调用ServiceProviderEngine对象的CreateScope创建的ServiceProviderEngineScope来说,由于它同时也是一个IServiceProvider对象,如果我们调用它的GetService<IServiceProvider>方法,该方法同样返回它自己。如果我们调用它的GetService<IServiceScopeFactory>方法,它返回创建它的ServiceProviderEngine对象,也就是该方法和Engine属性返回同一个对象。

4-13

依赖注入框架提供的服务实例最终是通过ServiceProviderEngine对象提供的。从上面给出的代码片段可以看出,ServiceProviderEngine是一个抽象类,.NET Core依赖注入框架提供了如下四个具体的实现类型,默认使用的是DynamicServiceProviderEngine。

  • RuntimeServiceProviderEngine:采用反射的方式提供服务实例;
  • ILEmitServiceProviderEngine:采用IL Emit的方式提供服务实例;
  • ExpressionsServiceProviderEngine:采用表达式树的方式提供服务实例;
  • DynamicServiceProviderEngine:根据请求并发数量动态决定最终的服务实例提供方案(反射和者IL Emit或者反射与表达式树,是否选择IL Emit取决于当前运行时是否支持Reflection Emit)。

4.4.2. ServiceProvider

调用IServiceCollection集合的扩展方法BuildServiceProvider创建的是一个ServiceProvider对象。作为根容器的ServiceProvider对象,和前面介绍的ServiceProviderEngine和ServiceProviderEngineScope对象,一起构建了整个依赖注入框架的设计蓝图。

4-14

在利用IServiceCollection集合创建ServiceProvider对象的时候,提供的服务注册将用来创建一个具体的ServiceProviderEngine对象。该ServiceProviderEngine对象的RootScope就是它创建的一个ServiceProviderEngineScope对象,子容器提供的Singleton服务实例由它维护。如果调用ServiceProvider对象的GetService<IServiceProvider>方法,返回的其实不是它自己,而是作为RootScope的ServiceProviderEngineScope对象(调用ServiceProviderEngineScope对象的GetService<IServiceProvider>方法返回的是它自己)。

ServiceProvider和ServiceProviderEngineScope都实现了IServiceProvider接口,如果我们调用了它们的GetService<IServiceScopeFactory>方法,返回的都是同一个ServiceProviderEngine对象。这一个特性决定了调用它们的CreateScope扩展方法都会创建一个新的ServiceProviderEngineScope对象作为子容器。综上所述,我们针对依赖注入框架总结出如下的特性:

  • ServiceProviderEngine的唯一性:整个服务提供体系只存在一个唯一的ServiceProviderEngine对象。
  • ServiceProviderEngine与IServiceFactory的同一性:唯一存在的ServiceProviderEngine会作为创建服务范围的IServiceFactory工厂。
  • ServiceProviderEngineScope和IServiceProvider的同一性:表示服务范围的ServiceProviderEngineScope同时也是作为服务提供者的依赖注入容器。

为了印证我们总结出来的特性,我们编写的测试代码。由于设计的ServiceProviderEngine和ServiceProviderEngineScope都是内部类型,我们只能采用反射的方式得到它们的属性或者字段成员。上面总结的这些特征体现在如下几组调试断言中。