Core官方DI解析(4)--CallSiteRuntimeResolver

`CallSiteRuntimeResolver`类型是一个创建或获取服务实例的类型,这个类型继承了`CallSiteVisitor<TArgument, TResult>`这个类型,也是使用了访问者模式,下面一一来解析此类

ServiceProviderEngineScope

在解析`CallSiteRuntimeResolver`之前先看一下`ServiceProviderEngineScope`类型,这个类型就可以是一个容器类型,最后实例化的服务对象就缓存在此类之中,
从下面代码中可以看出此类实现了`IServiceScope`和`IServiceProvider`两个接口,并且此类型拥有两个字段

_disposables:IDisposabl集合,此字段缓存的时所有实现了IDisposable接口的注册服务,以便在释放此容器实例时并将这些服务一起释放

_disposed:判断此属性是否已被是否释放

internal class ServiceProviderEngineScope : IServiceScope, IServiceProvide

{

       private List<IDisposable> \_disposables;

       private bool \_disposed;

}
在此类中还具有两个属性,一个是缓存实例对象的集合和一个\*\*ServiceProviderEngine\*\*类型的属性,从下面可以看出缓存集合使用了是`ServiceCacheKey`作为缓存的key,
而Engine是引擎类型,此属性通过构造函数传入,并且所有容器共享一个`ServiceProviderEngine`,也就是共享容器共享注册的服务    
//    缓存的实例对象集合

internal Dictionary<ServiceCacheKey, object> ResolvedServices { get; } = new Dictionary<ServiceCacheKey, object>();

//    所有ServiceProviderEngineScope对象共享一个ServiceProviderEngine

public ServiceProviderEngine Engine { get; }



//     构造函数

 public ServiceProviderEngineScope(ServiceProviderEngine engine)=> Engine = engine;
这个类中一共具有四个方法,

* GetService():获取对象,可以看到此方法调用的**Engine**的**GetService()**,这个方法到ServiceProviderEngine时再看

* ServiceProvider():这个方法返回的是当前对象

* Dispose():释放当前容器,可以看到在释放当前容器时会把**_disposables**集合中所有实例进行释放,并把**_disposed**属性设置*TRUE*

* CaptureDisposable():这个方法缓存要被的释放的服务实例

public object GetService(Type serviceType)

{

     if (\_disposed)

          //        如果已被释放,就不能调用此方法

          ThrowHelper.ThrowObjectDisposedException();

     return Engine.GetService(serviceType, this);

}



public IServiceProvider ServiceProvider => this;



public void Dispose()

{

     lock (ResolvedServices)

     {

          if (\_disposed)

               return;

          \_disposed = true;

          if (\_disposables != null)

          {

               for (var i = \_disposables.Count - 1; i >= 0; i--)

               {

                    var disposable = \_disposables[i];

                    disposable.Dispose();

               }



               \_disposables.Clear();

          }



          ResolvedServices.Clear();

     }

}

//  缓存所有需要清理的服务实例

internal object CaptureDisposable(object service)

{



     if (!ReferenceEquals(this, service))

     {

          if (service is IDisposable disposable)

          {

               lock (ResolvedServices)

               {

                    if (\_disposables == null)

                         \_disposables = new List<IDisposable>();

                    \_disposables.Add(disposable);

               }

          }

     }

     return service;

}

CallSiteRuntimeResolve

上面说过`CallSiteRuntimeResolver`这个类型是创建和获取服务实例类型的访问者,这个类型泛型参数分别为`RuntimeResolverContext`类型和实例对象类型`Object`
internal sealed class CallSiteRuntimeResolver : CallSiteVisitor<RuntimeResolverContext, object>{}
`RuntimeResolverContext`类型是一个`ServiceProviderEngineScope`封装类型,这个类型中具有一个`ServiceProviderEngineScope`类型属性和一个`RuntimeResolverLock`枚举类型属性,这个枚举类型在实例化对象时当做了锁使用
internal struct RuntimeResolverContext

{

     public ServiceProviderEngineScope Scope { get; set; }

     //        锁

     public RuntimeResolverLock AcquiredLocks { get; set; }

}

[Flags]

internal enum RuntimeResolverLock

{

     Scope = 1,

     Root = 2

}

CallSiteRuntimeResolver类型中拥有两类方法,

* 根据注册服务的生命周期进行访问服务实例对象

* 根据ServiceCallSite的设置类型进行访问服务实例对象

这两个类都在其CallSiteVisitor<TArgument, TResult>基类中

//        根据服务对象的生命周期进行访问访问实例

protected virtual TResult VisitCallSite(ServiceCallSite callSite, TArgument argument)

{

     //    缓存位置由ServiceCallSite内部的Cache属性的Location提供

     switch (callSite.Cache.Location)

     {

          case CallSiteResultCacheLocation.Root:

               return VisitRootCache(callSite, argument);

          case CallSiteResultCacheLocation.Scope:

               return VisitScopeCache(callSite, argument);

          case CallSiteResultCacheLocation.Dispose:

               return VisitDisposeCache(callSite, argument);

          case CallSiteResultCacheLocation.None:

               return VisitNoCache(callSite, argument);

          default:

               throw new ArgumentOutOfRangeException();

     }

}



//        根据其ServiceCallSite的Kind属性访问服务对象

protected virtual TResult VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)

{

     switch (callSite.Kind)

     {

          case CallSiteKind.Factory:

               return VisitFactory((FactoryCallSite)callSite, argument);

          case  CallSiteKind.IEnumerable:

               return VisitIEnumerable((IEnumerableCallSite)callSite, argument);

          case CallSiteKind.Constructor:

               return VisitConstructor((ConstructorCallSite)callSite, argument);

          case CallSiteKind.Constant:

               return VisitConstant((ConstantCallSite)callSite, argument);

          case CallSiteKind.ServiceProvider:

               return VisitServiceProvider((ServiceProviderCallSite)callSite, argument);

          case CallSiteKind.ServiceScopeFactory:

               return VisitServiceScopeFactory((ServiceScopeFactoryCallSite)callSite, argument);

          default:

               throw new NotSupportedException($"Call site type {callSite.GetType()} is not supported");

     }

}

这两个方法内部调用的方法部分被CallSiteRuntimeResolver类中重写,

下面先来看看根据生命周期进行访问的一系列方法

* ##### VistRootCache:

   这个方法是访问\*\*Root\*\*生命周期的方法,可以看到这个在这个方法调用了一个\*\*VisitCache()\*\*,这个方法一共四个参数,第一个,第二个分别是当前方法函数。第三个参数代表容器对象,容器使用的是`ServiceProviderEngine`实例中的\*\*Root\*\*属性,这个容器代表了顶级容器,<span style="color:red">\*\*这也就是Root生命周期的本质,使用的顶级容器进行创建/获取实例\*\*</span>,第四个参数锁,此方法使用的是\*\*RuntimeResolverLock.Root\*\*锁

* ##### VisitScopeCache:

   这个方法是访问\*\*Scoped\*\*生命周期方法,此方法和上面方法相似,也是调用了\*\*VisitCache()\*\*,但是不同的是是锁不同,这个锁是根据当前容器来决定,如果当前容器为顶级容器,就使用\*\*Root\*\*锁,所以不为顶级容器,则使用\*\*Scope\*\*锁

* ##### VisitDisposeCache

  这个方法访问\*\*transient\*\*生命周期方法,可以看到这个方法直接调用\*\*VisitCallSiteMain()\*\*进行获取实例对象,然后调用\*\*CaptureDisposable()\*\*将此对象尝试缓存到\*\*ServiceProviderEngineScope\*\*容器的\*\*\_disposables\*\*集合中

* ##### VisitNoCache

  这个方法代表不缓存,这个方法在`CallSiteRuntimeResolver`类中未重写,所以直接调用的`CallSiteVisitor`类型的\*\*VisitNoCache()\*\*,也基类中直接调用\*\*VisitCallSiteMain()\*\*
////        CallSiteRuntimeResolve

//        访问Root生命周期方法

protected override object VisitRootCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)

      => VisitCache(singletonCallSite, context, context.Scope.Engine.Root, RuntimeResolverLock.Root);

//        访问Scoped生命周期方法

protected override object VisitScopeCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)

{

     //      如果当前容器为根容器,则将其锁转换为Root,否则为Scope

     var requiredScope = context.Scope == context.Scope.Engine.Root ?

          RuntimeResolverLock.Root :

     RuntimeResolverLock.Scope;

     return VisitCache(singletonCallSite, context, context.Scope, requiredScope);

}

//        访问transient生命周期方法

protected override object VisitDisposeCache(ServiceCallSite transientCallSite, RuntimeResolverContext context)

      => context.Scope.CaptureDisposable(VisitCallSiteMain(transientCallSite, context));



////        CallSiteVisito

//        无缓存

 protected virtual TResult VisitNoCache(ServiceCallSite callSite, TArgument argument)

      => VisitCallSiteMain(callSite, argument);
\*\*VisitCache()\*\*这个方法是使用指定的容器进行实例化并缓存服务实例对象,在下面代码中可以看到,代码中根据\*\*RuntimeResolverContext\*\*实例的枚举值与第四个参数进行,如果不相同,则进行加锁。然后进行获取实例服务对象,如果已缓存则直接获取,没有缓存则调用\*\*VisitCallSiteMain()\*\*获取实例并缓存
private object VisitCache(ServiceCallSite scopedCallSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)

{

     bool lockTaken = false;

     //      获取容器中的缓存服务实例属性

     var resolvedServices = serviceProviderEngine.ResolvedServices;

     if ((context.AcquiredLocks & lockType) == 0)

            //      如果当前枚举值与RuntimeResolverContext的枚举值不相同,则加锁

          Monitor.Enter(resolvedServices, ref lockTaken);

     try

     {

          //      如果当前数据并未在缓存之中,则实例化此对象并将其缓存至集合中

          if (!resolvedServices.TryGetValue(scopedCallSite.Cache.Key, out var resolved))

          {

               //      获取实例对象

               resolved = VisitCallSiteMain(scopedCallSite, new RuntimeResolverContext

                                            {

                                                 Scope = serviceProviderEngine,

                                                 AcquiredLocks = context.AcquiredLocks | lockType

                                            });

               //      将当前对象尝试加入到容器的\_disposables集合

               serviceProviderEngine.CaptureDisposable(resolved);

               //      缓存实例对象

               resolvedServices.Add(scopedCallSite.Cache.Key, resolved);

          }

          return resolved;

     }

     finally

     {

          if (lockTaken)

              Monitor.Exit(resolvedServices);

     }

}
\*\*VisitCallSiteMain()\*\*内调用的所有方法都在`CallSiteRuntimeResolver`类进行了重写,下面看看`CallSiteRuntimeResolve`类中的这些方法

* ##### VisitFactory

在*VisitFactory()*中直接调用了FactoryCallSite实例对象的工厂方法获取实例

* ##### VisitIEnumerable

在*VisitIEnumerable()*中实例了IEnumerableCallSite中**ServiceCallSites**集合的所有对象,并组装到一个数组进行返回

* ##### ConstructorCallSite

在*VisitConstructor()*中使用反射方法实例化对象,并且如果构造函数不为空则获取所有参数的实例对象

* ##### ConstantCallSite

在*VisitConstant()*中直接返回了ConstantCallSite中的对象

* ##### VisitServiceProvide

在*VisitServiceProvider()*直接返回了RuntimeResolverContext封装的容器

* ##### VisitServiceScopeFactory

在*VisitServiceScopeFactory()*中则直接返回了容器实例中引擎对象(**ServiceProviderEngine**)

//      FactoryCallSite

protected override object VisitFactory(FactoryCallSite factoryCallSite, RuntimeResolverContext context)

     //        调用工厂方法进行实例化

     => factoryCallSite.Factory(context.Scope);



//        IEnumerableCallSite

protected override object VisitIEnumerable(IEnumerableCallSite enumerableCallSite, RuntimeResolverContext context)

{

     var array = Array.CreateInstance(

          enumerableCallSite.ItemType,

          enumerableCallSite.ServiceCallSites.Length);

     for (var index = 0; index < enumerableCallSite.ServiceCallSites.Length; index++)

     {

          //        实例化IEnumerableCallSite.ServiceCallSites中所有的服务实例对象并赋值到数组中

          var value = VisitCallSite(enumerableCallSite.ServiceCallSites[index], context);

          array.SetValue(value, index);

     }

     return array;

}



//        ConstructorCallSite

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);

     }

     try

     {

          //        根据参数对象进行实例化对象并返回

          return constructorCallSite.ConstructorInfo.Invoke(parameterValues);

     }

     catch (Exception ex) when (ex.InnerException != null)

     {

          ExceptionDispatchInfo.Capture(ex.InnerException).Throw();

          // The above line will always throw, but the compiler requires we throw explicitly.

          throw;

     }

}



//        ConstantCallSite

 protected override object VisitConstant(ConstantCallSite constantCallSite, RuntimeResolverContext context)

      //        直接返回ConstantCallSite的值

      => constantCallSite.DefaultValue;



//        ServiceProviderCallSite

protected override object VisitServiceProvider(ServiceProviderCallSite serviceProviderCallSite, RuntimeResolverContext context) 

     //        直接返回RuntimeResolverContext封装的容器

     => context.Scope;



//        ServiceScopeFactoryCallSite

 protected override object VisitServiceScopeFactory(ServiceScopeFactoryCallSite serviceScopeFactoryCallSite, RuntimeResolverContext context)

      //        直接返回容器内的ServiceProviderEngine

      => context.Scope.Engine;
在`CallSiteRuntimeResolver`中还有叫做\*\*Resolve()\*\*,这个方法则是外部调用的,这个方法是由一个`ServiceCallSite`对象和一个容器对象`ServiceProviderEngineScope`,然后直接调用\*\*VisitCallSite()\*\*进行方法,可以看到调用此方法时\*\*AcquiredLocks\*\*属性并未赋值.
public object Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)

{

     return VisitCallSite(callSite, new RuntimeResolverContext

                          {

                               Scope = scope

                          });

}

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Android开发实战

设计模式-单例模式

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。

843
来自专栏博客园

Core官方DI解析(4)--CallSiteRuntimeResolver

​ CallSiteRuntimeResolver类型是一个创建或获取服务实例的类型,这个类型继承了CallSiteVisitor<TArgument, TRe...

991
来自专栏null的专栏

数据结构和算法——旋转打印链表

1、问题描述 输入参数nnn为正整数,如输入n=5n=5n=5,则按行打印如下的数字: ? 2、问题的理解 这个问题是将数字1…n21…n21\dots n^2...

3063
来自专栏JavaEdge

设计实现一个LRU Cache1 什么是LRU Cache2 实现思路

3807
来自专栏开发与安全

算法:静态查找表(Static Search Table)(顺序查找、二分查找、插值查找、斐波纳契查找)

查找表(Search table)是由同一类型的数据元素(或记录)构成的集合。关键字(key)是数据元素中某个数据项的值,又称为键值,用它可以表示一个数据元素,...

2455
来自专栏Jackson0714

PHP内核之旅-3.变量

1524
来自专栏刘望舒

Kotlin下的5种单例模式

5021
来自专栏从零开始学 Web 前端

嵌入式经典面试题

一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重...

3563
来自专栏数据之美

那些 Shell、Awk 中自动隐式类型转换的“坑”

1、问题: 在林林总总的编程语言里,弱类型的语言着实不少,一方面这种“动态类型”用起来很方便,而另一方面则“坑”你没商量~ 常见的 SQL、Shell、A...

2225
来自专栏青玉伏案

iOS开发之SQLite--C语言接口规范(三)——Binding Values To Prepared Statements

  在前面的博客中已经介绍了如何连接SQLite数据库,并且简单的查询和遍历结果集。在前面用到了sqlite3_stmt *stmt,也就是预编译后的SQL语句...

1936

扫码关注云+社区

领取腾讯云代金券