关于我们

质量为本、客户为根、勇于拼搏、务实创新

< 返回新闻公共列表

.net core 轻量级容器 ServiceProvider 源码分析

发布时间:2020-02-24 00:00:00
  1. 首先看 ServiceCollection 的定义
    //定义public class ServiceCollection : IServiceCollection
    {     private readonly List_descriptors = new List();
    
         ......
    }//接口定义public interface IServiceCollection : IList{
    }

    由此可见,ServiceCollection 本身是一个 List 的集合,下面我们来看一下 ServiceDescriptor 的定义

    public class ServiceDescriptor
    {//重要的构造函数public ServiceDescriptor(Type serviceType, Type implementationType, ServiceLifetime lifetime)
        {
        }//重要的属性/// /// Service 的生命周期/// /// public ServiceLifetime Lifetime { get; }/// /// Service 的类型/// /// public Type ServiceType { get; }/// /// Service 的实现类型/// /// public Type ImplementationType { get; }/// /// Service 对象/// /// public object ImplementationInstance { get; }/// /// 创建 Service 对象的工厂/// /// public Func<IServiceProvider, object> ImplementationFactory { get; }
    
        ......
    }

    ServiceDescriptor 保存了 Service 类型和 Service 对象之间的关系以及 Service 的生命周期,下面来看一下 Service 的生命周期

    public enum ServiceLifetime
    {/// /// 单例///     Singleton,/// /// 范围内/// /// /// 在 ASP.NET Core 应用中,每一个请求会创建一个范围///     Scoped,/// /// 瞬时///     Transient
    }

    再来看一下 IServiceCollection 提供的一些拓展方法

    public static class ServiceCollectionServiceExtensions
    {//基本是3中形式,都是简单的封装public static IServiceCollection AddSingleton(this IServiceCollection services, ...)public static IServiceCollection AddScoped(this IServiceCollection services, ...)public static IServiceCollection AddTransient(this IServiceCollection services, ...)
    
        ......//最终都会调用同一个方法private static IServiceCollection Add(
                IServiceCollection collection,
                Type serviceType,
                Type implementationType,
                ServiceLifetime lifetime)
            {var descriptor = new ServiceDescriptor(serviceType, implementationType, lifetime);
                collection.Add(descriptor);return collection;
            }
    }

    这些方法的作用都是为了填充 ServiceCollection 中的 _descriptors 字段,IServiceCollection 有一个特别重要的方法,BuildServiceProvider,创建 ServiceProvider 

        ServiceProvider BuildServiceProvider( (services ==   (options ==

     

  2. ServiceProvider,Service 的提供者,这是一个非常重要的类,也是容器的核心,主要用来创建 Service 对象的实例
    public sealed class ServiceProvider : IServiceProvider, IDisposable{
      
      //
    ServiceProvider 引擎private readonly IServiceProviderEngine _engine;//构造函数internal ServiceProvider(IEnumerable

    由此可见,ServiceProvider 创建对象的过程由 ServiceProviderEngine 接管,而 Engine 有4种,分别是 DynamicServiceProviderEngine,RuntimeServiceProviderEngine,ILEmitServiceProviderEngine,ExpressionsServiceProviderEngine,下面是他们之间的关系,

  3. 由上图可知,ServiceProvider 的最终的核心实现应该在 ServiceProviderEngine 这个抽象类中,下面我们来看一下这个类,我去掉了一些判断和记录日志的逻辑,让代码看起来更简洁
        Func<Type, Func<ServiceProviderEngineScope, >> ServiceProviderEngine(IEnumerable

    这个类中有几个特别重要的对象,

    1. RuntimeResolver ,Service 对象的创建就是这个对象完成的,当然不同的子类,有不同的实现,
      internal class DynamicServiceProviderEngine : CompiledServiceProviderEngine
      {//该类本身并没有定义 RutimeResolver 而是通过父类 CompiledServiceProviderEngine 的 ResolverBuilder 实现的}internal abstract class CompiledServiceProviderEngine : ServiceProviderEngine
      {//通过编译条件变量来确定是使用 ILEmit 还是使用 Expression#if IL_EMITpublic ILEmitResolverBuilder ResolverBuilder { get; }#elsepublic ExpressionResolverBuilder ResolverBuilder { get; }#endifpublic CompiledServiceProviderEngine(IEnumerableserviceDescriptors, IServiceProviderEngineCallback callback) : base(serviceDescriptors, callback)
          {#if IL_EMITResolverBuilder = new ILEmitResolverBuilder(RuntimeResolver, this, Root);#elseResolverBuilder = new ExpressionResolverBuilder(RuntimeResolver, this, Root);#endif}
      
          ......
      }internal class RuntimeServiceProviderEngine : ServiceProviderEngine
      {//该类本身没有对应的 RuntimeResolver,直接使用父类默认的 CallSiteRuntimeResolver}internal class ILEmitServiceProviderEngine : ServiceProviderEngine
      {private readonly ILEmitResolverBuilder _expressionResolverBuilder;public ILEmitServiceProviderEngine(IEnumerableserviceDescriptors, IServiceProviderEngineCallback callback) : base(serviceDescriptors, callback)
          {
              _expressionResolverBuilder = new ILEmitResolverBuilder(RuntimeResolver, this, Root);
          }
      
          ......
      }internal class ExpressionsServiceProviderEngine : ServiceProviderEngine
      {private readonly ExpressionResolverBuilder _expressionResolverBuilder;public ExpressionsServiceProviderEngine(IEnumerableserviceDescriptors, IServiceProviderEngineCallback callback) : base(serviceDescriptors, callback)
          {
              _expressionResolverBuilder = new ExpressionResolverBuilder(RuntimeResolver, this, Root);
          }
        
          ......
      }

      所以总结来看,有3个对应的 Resolver 分别是:CallSiteRuntimeResolver,ILEmitResolverBuilder,ExpressionResolverBuilder 这3个类都继承于 CallSiteVisitor 的泛型类,只是对应的泛型参数不太一样

    2. CallSiteFactory,调用目标工厂,主要用来根据 ServiceDescriptor 的定义创建对应的 ServiceCallSite 对象,然后根据该对象来创建 Service 的实例,这个对象比较复杂,下面来看一些简洁的源码
      internal class CallSiteFactory
      {private const int DefaultSlot = 0;private readonly List _descriptors;private readonly Dictionary_descriptorLookup = new Dictionary();public CallSiteFactory(IEnumerable descriptors)
          {
              _descriptors = descriptors.ToList();
              Populate();
          }private void Populate()
          {/*在实例化 CallSiteFactory 对象时,会将 ServiceDescriptor 对象转换成字典 Dictionary,
              ServiceDescriptorCacheItem 用来将同一个 ServiceType 的 ServiceDescriptor 聚合在一起,其中 ServiceDescriptorCacheItem 
              的 Last 属性,是取最后一个 ServiceDescriptor,这也就是为什么,我们 Add 同一个类型的多个实例时,获取当前类型的实例时,返回的是最后一个实例的原因*/foreach (var descriptor in _descriptors)
              {var cacheKey = descriptor.ServiceType;
                  _descriptorLookup.TryGetValue(cacheKey, out var cacheItem);
                  _descriptorLookup[cacheKey] = cacheItem.Add(descriptor);
              }
          }
      }private struct ServiceDescriptorCacheItem
      {private List _items;public ServiceDescriptor Last
          {get{return _items[_items.Count - 1];
              }
          }public ServiceDescriptorCacheItem Add(ServiceDescriptor descriptor)
          {var newCacheItem = new ServiceDescriptorCacheItem();
      
              newCacheItem._item = _item;
              newCacheItem._items = _items ?? new List();
              newCacheItem._items.Add(descriptor);return newCacheItem;
          }
      }

      还有几个比较关键的方法,下面来看一下代码

       callSite = TryCreateExact(serviceType, callSiteChain) ???? (_descriptorLookup.TryGetValue(serviceType,    ServiceCallSite TryCreateExact(ServiceDescriptor descriptor, Type serviceType, CallSiteChain callSiteChain,  (serviceType == (descriptor.ImplementationInstance != callSite =   (descriptor.ImplementationFactory != callSite =   (descriptor.ImplementationType != callSite =  ServiceCallSite TryCreateOpenGeneric(ServiceDescriptor descriptor, Type serviceType, CallSiteChain callSiteChain,  (serviceType.IsConstructedGenericType && serviceType.GetGenericTypeDefinition() == closedType =  (serviceType.IsConstructedGenericType && serviceType.GetGenericTypeDefinition() == (IEnumerable<> itemType = callSites =  List
  4. 由于 ServiceProvider 容器本身只支持构造函数注入,所以我们主要关注每个 Resolver 的 VisitConstructor 方法,

    1. CallSiteRuntimeResolver (.net framework 4.6.2 + 默认使用的方式)

      internal sealed class CallSiteRuntimeResolver : CallSiteVisitor<RuntimeResolverContext, object>{protected override object VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
          {object[] parameterValues;if (constructorCallSite.ParameterCallSites.Length == 0)
              {
                  parameterValues = Array.Empty<object>();
              }else{//循环获取每个参数类型的实例,如果参数类型还依赖于其它的类型,则会递归获取parameterValues = new object[constructorCallSite.ParameterCallSites.Length];for (var index = 0; index < parameterValues.Length; index++)
                  {
                      parameterValues[index] = VisitCallSite(constructorCallSite.ParameterCallSites[index], context);
                  }
              }return constructorCallSite.ConstructorInfo.Invoke(parameterValues);
          }
      }
    2. ILEmitResolverBuilder (.net core 默认使用方式)
      /*由于 IL 我懂的也不是很多,只是大概知道,需要把参数提前准备好放在堆栈上,然后调用 Newobj 就可以实例化对象,
      源码很长,有兴趣想要研究的小伙伴,可以自行学习*/protected override object VisitConstructor(ConstructorCallSite constructorCallSite, ILEmitResolverBuilderContext argument)
      {foreach (var parameterCallSite in constructorCallSite.ParameterCallSites)
          {
              VisitCallSite(parameterCallSite, argument);
          }
          argument.Generator.Emit(OpCodes.Newobj, constructorCallSite.ConstructorInfo);return null;
      }private GeneratedMethod BuildTypeNoCache(ServiceCallSite callSite)
      {//动态创建方法var dynamicMethod = new DynamicMethod("ResolveService",
              attributes : MethodAttributes.Public | MethodAttributes.Static,
              callingConvention : CallingConventions.Standard,
              returnType : typeof(object),
              parameterTypes : new [] { typeof(ILEmitResolverBuilderRuntimeContext), typeof(ServiceProviderEngineScope) },
              owner : GetType(),
              skipVisibility : true);var info = ILEmitCallSiteAnalyzer.Instance.CollectGenerationInfo(callSite);var ilGenerator = dynamicMethod.GetILGenerator(info.Size);//创建方法体var runtimeContext = GenerateMethodBody(callSite, ilGenerator);return new GeneratedMethod()
          {
              Lambda = (Func<ServiceProviderEngineScope, object>) dynamicMethod.CreateDelegate(typeof(Func<ServiceProviderEngineScope, object>), runtimeContext),
              Context = runtimeContext,
              DynamicMethod = dynamicMethod
          };
      }
    3. ExpressionResolverBuilder 可以理解为使用表达式树将 CallSiteRuntimeResolver 的代码翻译了一遍
      internal class ExpressionResolverBuilder : CallSiteVisitor<object, Expression>{protected override Expression VisitConstructor(ConstructorCallSite callSite, object context)
          {var parameters = callSite.ConstructorInfo.GetParameters();
              Expression[] parameterExpressions;if (callSite.ParameterCallSites.Length == 0)
              {
                  parameterExpressions = Array.Empty();
              }else{//循环每一个参数,根据参数创建表达式parameterExpressions = new Expression[callSite.ParameterCallSites.Length];for (int i = 0; i < parameterExpressions.Length; i++)
                  {
                      parameterExpressions[i] = Convert(VisitCallSite(callSite.ParameterCallSites[i], context), parameters[i].ParameterType);
                  }
              }return Expression.New(callSite.ConstructorInfo, parameterExpressions);
          }
      }
  5. 总结一下
    1. ServiceCollection 只不过是用来定义 Service 的类型和定义以及生命周期
    2. Service 类型的创建是最终是通过不同的 RuntimeResolver 来实现的
    3. 源码中还包含大量对缓存的使用,如果没有缓存,这个容器的效率也就太低了,我在分析源码的时候直接略过了,原因是,我觉得缓存一定是在先实现后的基础上再加的,所以我们研究源码的过程中可以先忽略这些缓存的使用
    4. 关于 Service 的生命周期我没有细讲,单例和瞬时都非常好理解,其实最复杂的就是 Scope,有点绕的地方在于维护 IDisposable 类型的资源的释放,当然这个理解起来也不是很难,有兴趣的小伙伴可以自行研究
    5. 源码分析没有讲最基本的 IOC 概念和容器概念,想要理解这些,前提是要对容器的概念非常了解才行

/template/Home/Zkeys/PC/Static