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

时间:2022-03-17 08:58:40

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

一、用于服务创建的ServiceCallSite

服务实例的创建方式主要有三种,分别对应ServiceDescriptor如下三个只读属性。简单来说,如果ImplementationInstance属性返回一个具体的对象,该对象将直接作为提供的服务实例。如果属性ImplementationFactory返回一个具体的委托对象,该委托将会作为提供服务实例的工厂。除此之外,ServiceProvider将会利用ImplementationType属性返回的真是服务类型定位某一个最佳的构造函数来创建最终提供的服务实例。

   1: public class ServiceDescriptor

   2: {

   3:     public Type                               ImplementationType {  get; }

   4:     public object                             ImplementationInstance {  get; }

   5:     public Func<IServiceProvider, object>     ImplementationFactory {  get; }      

   6: }

服务实例的这三种不同的创建方式最终由三种对应的ServiceCallSite类型来完成,我们将它们的类型分别命名为InstanceCallSite、FactoryCallSite和ConstructorCallSite。如下面的代码片段所示,前两种ServiceCallSite(InstanceCallSite和FactoryCallSite)的实现非常简单,所以我们在这里就不对它们多做介绍了。

   1: internal class InstanceCallSite : IServiceCallSite

   2: {

   3: public object Instance { get; private set; }

   4:  

   5:     public InstanceCallSite(object instance)

   6:     {

   7:         this.Instance = instance;

   8:     }

   9:     public Expression Build(Expression provider)

  10:     {

  11:         return Expression.Constant(this.Instance);

  12:     }

  13:     public object Invoke(ServiceProvider provider)

  14:     {

  15:         return Instance;

  16:     }

  17: }

  18:  

  19: internal class FactoryCallSite : IServiceCallSite

  20: {

  21:     public Func<IServiceProvider, object> Factory { get; private set; }

  22:     public FactoryCallSite(Func<IServiceProvider, object> factory)

  23:     {

  24:         this.Factory = factory;

  25:     }

  26:     public Expression Build(Expression provider)

  27:     {

  28:         Expression<Func<IServiceProvider, object>> factory = p => this.Factory(p);

  29:         return Expression.Invoke(factory, provider);

  30:     }

  31:     public object Invoke(ServiceProvider provider)

  32:     {

  33:         return this.Factory(provider);

  34:     }

  35: }

以执行指定构造函数创建服务实例的ConstructorCallSite稍微复杂一点。如下面的代码片段所示,我们在创建一个ConstructorCallSite对象的时候除了指定一个代表构造函数的ConstructorInfo对象之外,还需要指定一组用于初始化对应参数列表的ServiceCallSite。

   1: internal class ConstructorCallSite : IServiceCallSite

   2: {

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

   4:     public IServiceCallSite[] Parameters { get; private set; }

   5:  

   6:     public ConstructorCallSite(ConstructorInfo constructorInfo, IServiceCallSite[] parameters)

   7:     {

   8:         this.ConstructorInfo = constructorInfo;

   9:         this.Parameters = parameters;

  10:     }

  11:  

  12:     public Expression Build(Expression provider)

  13:     {

  14:         ParameterInfo[] parameters = this.ConstructorInfo.GetParameters();

  15:         return Expression.New(this.ConstructorInfo, this.Parameters.Select((p, index) => Expression.Convert(p.Build(provider), 

  16:             parameters[index].ParameterType)).ToArray());

  17:     }

  18:  

  19:     public object Invoke(ServiceProvider provider)

  20:     {

  21:         return this.ConstructorInfo.Invoke(this.Parameters.Select(p => p.Invoke(provider)).ToArray());

  22:     }

  23: }

虽然ConstructorCallSite自身创建服务实例的逻辑很简单,但是如何创建ConstructorCallSite对象本身相对麻烦一些,因为这涉及到如何选择一个最终构造函数的问题。我们在上面专门介绍过这个问题,并且总结出选择构造函数采用的两条基本的策略:

  • ServiceProvider能够提供构造函数的所有参数。
  • 目标构造函数的参数类型集合是所有有效构造函数参数类型集合的超级。

我们将ConstructorCallSite的创建定义在Service类的CreateConstructorCallSite方法中,它具有额外两个辅助方法GetConstructor和GetParameterCallSites,前者用于选择正确的构造函数,后者则为指定的构造函数创建用于初始化参数的ServiceCallSite列表。

   1: internal class Service : IService

   2: {

   3:     private ConstructorCallSite CreateConstructorCallSite(ServiceProvider provider, ISet<Type> callSiteChain)

   4:     {

   5:         ConstructorInfo constructor = this.GetConstructor(provider, callSiteChain);

   6:         if (null == constructor)

   7:         {

   8:             throw new InvalidOperationException("No avaliable constructor");

   9:         }

  10:         return new ConstructorCallSite(constructor, constructor.GetParameters().Select(p => provider.GetServiceCallSite(p.ParameterType, callSiteChain)).ToArray());                                              

  11: }

  12:  

  13:     private ConstructorInfo GetConstructor(ServiceProvider provider, ISet<Type> callSiteChain)

  14:     {

  15:         ConstructorInfo[] constructors = this.ServiceDescriptor.ImplementationType.GetConstructors()

  16:             .Where(c => (null != this.GetParameterCallSites(c, provider, callSiteChain))).ToArray();

  17:  

  18:         Type[] allParameterTypes = constructors.SelectMany(

  19:             c => c.GetParameters().Select(p => p.ParameterType)).Distinct().ToArray();

  20:  

  21:         return constructors.FirstOrDefault(

  22:             c => new HashSet<Type>(c.GetParameters().Select(p => p.ParameterType)).IsSupersetOf(allParameterTypes));

  23:     }

  24:  

  25:     private IServiceCallSite[] GetParameterCallSites(ConstructorInfo constructor,ServiceProvider provider,ISet<Type> callSiteChain)

  26:     {

  27:         ParameterInfo[] parameters = constructor.GetParameters();

  28:         IServiceCallSite[] serviceCallSites = new IServiceCallSite[parameters.Length];

  29:  

  30:         for (int index = 0; index < serviceCallSites.Length; index++)

  31:         {

  32:             ParameterInfo parameter = parameters[index];

  33:             IServiceCallSite serviceCallSite = provider.GetServiceCallSite(

  34:                 parameter.ParameterType, callSiteChain);

  35:             if (null == serviceCallSite && parameter.HasDefaultValue)

  36:             {

  37:                 serviceCallSite = new InstanceCallSite(parameter.DefaultValue);

  38:             }

  39:             if (null == serviceCallSite)

  40:             {

  41:                 return null;

  42:             }

  43:             serviceCallSites[index] = serviceCallSite;

  44:         }

  45:         return serviceCallSites;

  46:     }

  47:     //其他成员

  48: }

二、用于管理生命周期的ServiceCallSite

服务实例最终采用何种提供方式还与服务注册时采用的生命周期管理模式有关,三种不同的生命周期管理模式(Transient、Scoped和Singleton)分别对应着三种不同的ServiceCallSite类型,我们分别将其命名为TransienCallSite、ScopedCallSite和SingletonCallSite。

对于TransientCallSite来说,它提供服务实例的方式非常直接,那就是直接创建一个新的对象。在此之外还需要考虑一个关于服务回收的问题,那就是如果服务实例对应的类型实现了IDisposable接口,需要将它添加到ServiceProvider的TransientDisposableServices属性中。TransientCallSite具有如下的定义,只读属性ServiceCallSite表示真正用于创建服务实例的ServiceCallSite。

   1: internal class TransientCallSite : IServiceCallSite

   2: {

   3:     public IServiceCallSite ServiceCallSite { get; private set; }

   4:     public TransientCallSite(IServiceCallSite serviceCallSite)

   5:     {

   6:         this.ServiceCallSite = serviceCallSite;

   7:     }

   8:  

   9:     public  Expression Build(Expression provider)

  10:     {

  11:         return Expression.Call(provider, "CaptureDisposable", null, this.ServiceCallSite.Build(provider));

  12:     }

  13:  

  14:     public  object Invoke(ServiceProvider provider)

  15:     {

  16:         return provider.CaptureDisposable(this.ServiceCallSite.Invoke(provider));

  17:     }

  18: }

  19:  

  20: internal class ServiceProvider : IServiceProvider, IDisposable

  21: {

  22:     

  23:     public object CaptureDisposable(object instance)

  24:     {

  25:         IDisposable disposable = instance as IDisposable;

  26:         if (null != disposable)

  27:         {

  28:             this.TransientDisposableServices.Add(disposable);

  29:         }

  30:         return instance;

  31:     }

  32:     //其他成员

  33: }

ScopedCallSite在提供服务实例的时候需要考虑当前ServiceProvider的ResolvedServices属性中是否已经存在已经提供的服务实例,如果已经存在它就无需重复创建。新创建的服务实例需要添加到当前ServiceProvider的ResolvedServices属性中。ScopedCallSite定义如下,ServiceCallSite属性依然表示的是真正用于创建服务实力的ServiceCallSite,Service属性则用来获取保存在当前ServiceProvider的ResolvedServices属性中的服务实例。

   1: internal class ScopedCallSite : IServiceCallSite

   2: {

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

   4:     public IServiceCallSite ServiceCallSite { get; private set; }

   5:  

   6:     public ScopedCallSite(IService service, IServiceCallSite serviceCallSite)

   7:     {

   8:         this.Service = service;

   9:         this.ServiceCallSite = serviceCallSite;

  10:     }

  11:  

  12:     public virtual Expression Build(Expression provider)

  13:     {

  14:         var service = Expression.Constant(this.Service);

  15:         var instance = Expression.Variable(typeof(object), "instance");

  16:         var resolvedServices = Expression.Property(provider, "ResolvedServices");

  17:         var tryGetValue = Expression.Call(resolvedServices, "TryGetValue", null, service, instance);

  18:         var index = Expression.MakeIndex(resolvedServices, typeof(ConcurrentDictionary<IService, object>).GetProperty("Item"), new Expression[] { service});

  19:         var assign = Expression.Assign(index, this.ServiceCallSite.Build(provider));

  20:  

  21:         return Expression.Block(typeof(object),new[] { instance },Expression.IfThen(Expression.Not(tryGetValue),assign),index);

  22:     }

  23:  

  24:     public virtual object Invoke(ServiceProvider provider)

  25:     {

  26:         object instance;

  27:         return provider.ResolvedServices.TryGetValue(this.Service, out instance)

  28:             ? instance

  29:             : provider.ResolvedServices[this.Service] = this.ServiceCallSite.Invoke(provider);

  30:     }

  31: }

其实Singleton和Scope这两种模式本质上是等同的,它们表示在某个ServiceScope中的“单例”模式,它们之间的不同之处在于前者的ServiceScope是针对作为根的ServiceProvider,后者则是针对当前ServiceProvider。所以SingletonCallSite是ScopedCallSite的派生类,具体定义如下所示。

   1: internal class SingletonCallSite : ScopedCallSite

   2: {

   3:     public SingletonCallSite(IService service, IServiceCallSite serviceCallSite) : 

   4:     base(service, serviceCallSite)

   5:     { }

   6:  

   7:     public override Expression Build(Expression provider)

   8:     {

   9:         return base.Build(Expression.Property(provider, "Root"));

  10:     }

  11:  

  12:     public override object Invoke(ServiceProvider provider)

  13:     {

  14:         return base.Invoke(provider.Root);

  15:     }

  16: }

三、创建ServiceCallSite

ServiceCallSite的创建体现在IService接口的CreateServiceSite方法中,在我们的Service类这个方法是如何实现的呢?如下面的代码片段所示,真正用于创建服务实例的ServiceCallSite先根据当前的ServiceDescriptor创建出来,然后再根据采用生命周期管理模式创建出与之匹配的ServiceCallSite。

   1: internal class Service : IService

   2: {

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

   4:     {

   5:         IServiceCallSite serviceCallSite = 

   6:             (null != this.ServiceDescriptor.ImplementationInstance)

   7:             ? new InstanceCallSite(this.ServiceDescriptor.ImplementationInstance)

   8:             : null;

   9:  

  10:         serviceCallSite = serviceCallSite?? 

  11:             ((null != this.ServiceDescriptor.ImplementationFactory)

  12:             ? new FactoryCallSite(this.ServiceDescriptor.ImplementationFactory)

  13:             : null);

  14:  

  15:         serviceCallSite = serviceCallSite ?? this.CreateConstructorCallSite(provider, callSiteChain);

  16:  

  17:         switch (this.Lifetime)

  18:         {

  19:             case ServiceLifetime.Transient: return new TransientCallSite(serviceCallSite);

  20:             case ServiceLifetime.Scoped: return new ScopedCallSite(this, serviceCallSite);

  21:             default: return new SingletonCallSite(this, serviceCallSite);

  22:         }

  23:     }

  24:     //其他成员    

  25: }

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实现揭秘【补充漏掉的细节】