Net6 DI原始碼分析Part3 CallSiteRuntimeResolver,CallSiteVisitor

一身大膘發表於2022-02-11
CallSiteRuntimeResolver

CallSiteRuntimeResolver是實現了CallSiteVisitor之一。
提供的方法主要分三個部分

  1. 自有成員方法

    1. Resolve提供服務
    2. VisitCache
    3. 私有建構函式
  2. 重寫父類方法

    1. VisitDisposeCache(ServiceCallSite transientCallSite, RuntimeResolverContext context)
      呼叫父類VisitCallSiteMain ,並把建立出來的服務新增到context.Scope.CaptureDisposable

      return context.Scope.CaptureDisposable(VisitCallSiteMain(transientCallSite, context));
      
    2. object VisitRootCache(ServiceCallSite callSite, RuntimeResolverContext context)

      1. 先找對應callSite.Value是否有值如果有直接返回
      2. 如果沒有直接呼叫 VisitCallSiteMain建立服務並把物件rootServiceProviderEngine內。
      3. 建立的服務放入callSite.Value。
          if (callSite.Value is object value) 
          {
              return value;
          }
      
      var lockType = RuntimeResolverLock.Root;
      ServiceProviderEngineScope serviceProviderEngine = context.Scope.RootProvider.Root;
      if (callSite.Value is object resolved)
      {
          return resolved;
      }
      resolved = VisitCallSiteMain(callSite, new RuntimeResolverContext
      {
          Scope = serviceProviderEngine,
          AcquiredLocks = context.AcquiredLocks | lockType
      });
      serviceProviderEngine.CaptureDisposable(resolved);
      callSite.Value = resolved;
      return resolved;
      
    3. VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)

      1. 先去serviceProviderEngine.resolvedServices拿,有的話直接返回如果沒拿到執行步驟2去建立。serviceProviderEngine.resolvedServices.TryGetValue(callSite.Cache.Key, out object resolved)
      2. VisitCallSiteMain建立物件
      3. 把建立後的物件加入對應Scope的 disposable列表
      4. 把建立後的物件加入對應Scope的 resolvedServices列表
          if (resolvedServices.TryGetValue(callSite.Cache.Key, out object resolved))
          {
              return resolved;
          }
          resolved = VisitCallSiteMain(callSite, new RuntimeResolverContext
          {
              Scope = serviceProviderEngine,
              AcquiredLocks = context.AcquiredLocks | lockType
          });
          serviceProviderEngine.CaptureDisposable(resolved);
          resolvedServices.Add(callSite.Cache.Key, resolved);
          return resolved;
      
  3. 實現父類抽象方法

    1. VisitConstructor
      1. 給構造方法提供引數(如果有引數遞迴呼叫VisitCallSite建立引數物件)
      2. 直接呼叫ServiceCallSite的ConstructorInfo反射建立物件。
         object[] parameterValues;
          if (constructorCallSite.ParameterCallSites.Length == 0)
          {
              parameterValues = Array.Empty<object>();
          }
          else
          {
              parameterValues = new object[constructorCallSite.ParameterCallSites.Length];
              for (int index = 0; index < parameterValues.Length; index++)
              {
                  parameterValues[index] = VisitCallSite(constructorCallSite.ParameterCallSites[index], context);
              }
          }
      
          return constructorCallSite.ConstructorInfo.Invoke(BindingFlags.DoNotWrapExceptions, binder: null, parameters: parameterValues, culture: null);
      
    2. override object VisitConstant(ConstantCallSite constantCallSite, RuntimeResolverContext context)
      直接返回CallSite物件的DefaultValue
      return constantCallSite.DefaultValue;
    3. VisitServiceProvider
      return context.Scope;
    4. override object VisitIEnumerable(IEnumerableCallSite enumerableCallSite, RuntimeResolverContext context)
      根據ServiceCallSites直接迴圈呼叫VisitCallSite建立例項,然後根據例項建立集合返回
        var array = Array.CreateInstance(
        enumerableCallSite.ItemType,
        enumerableCallSite.ServiceCallSites.Length);
        for (int index = 0; index < enumerableCallSite.ServiceCallSites.Length; index++)
        {
            object value = VisitCallSite(enumerableCallSite.ServiceCallSites[index], context);
            array.SetValue(value, index);
        }
        return array;
      
    5. VisitFactory
      return factoryCallSite.Factory(context.Scope);

體力活都給CallSiteFactory

CallSiteVisitor
  1. 方法
    1. VisitCallSite(ServiceCallSite callSite, TArgument argument)

      1. 保證Stack安全
      2. 根據callSite的Cache.Location去呼叫不同策略的Cache方法。
      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();
       }
      
    2. VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)

      1. 根據不同CallSiteKind去呼叫不同策略的建立服務方法。
         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);
              default:
                  throw new NotSupportedException(SR.Format(SR.CallSiteTypeNotSupported, callSite.GetType()));
          }
        
  2. 虛方法
    1. TResult VisitNoCache(ServiceCallSite callSite, TArgument argument) => VisitCallSiteMain(callSite, argument);
    2. TResult VisitDisposeCache(ServiceCallSite callSite, TArgument argument) -> isitCallSiteMain(callSite, argument);
    3. TResult VisitRootCache(ServiceCallSite callSite, TArgument argument) -> VisitCallSiteMain(callSite, argument);
    4. TResult VisitScopeCache(ServiceCallSite callSite, TArgument argument) => VisitCallSiteMain(callSite, argument);

    根據生命週期策略由子類實現對應儲存機制。

  3. 抽象方法
    1. TResult VisitConstructor(ConstructorCallSite constructorCallSite, TArgument argument);
    2. TResult VisitConstant(ConstantCallSite constantCallSite, TArgument argument);
    3. TResult VisitServiceProvider(ServiceProviderCallSite serviceProviderCallSite, TArgument argument);
    4. TResult VisitIEnumerable(IEnumerableCallSite enumerableCallSite, TArgument argument);
    5. TResult VisitFactory(FactoryCallSite factoryCallSite, TArgument argument);

    根據服務註冊時的建立機制策略由子類實現對應的建立物件機制。

CallSiteVisitor負責這活怎麼幹!。CallSiteRuntimeResolver負責幹活

  1. 呼叫 VisitCallSite在內部區分出scope並呼叫具體實現方法快取方法 (CallSiteResultCacheLocation),
    -> VisitCache方法
    -> 但是在快取前得先得到被快取的物件,這時通過呼叫(VisitCallSiteMain)建立物件
    -> VisitCallSiteMain根據提供的callsite型別去呼叫實現的抽象方法建立對應物件
    -> 根據cache對應的方法快取步驟1.1的結果。
  2. return cahce的結果。
整體的獲取物件流程大約如下

CallSiteRuntimeResolver.Resolve -> CallSiteVisitor->VisitCallSite callSite.Cache.Location VisitRootCache/VisitScopeCache/ VisitDisposeCache/ VisitNoCache -> CallSiteRuntimeResolver.VisitCache/ VisitRootCache -> VisitRootCache -> CallSiteVisitor.VisitCallSiteMain 根據callSite.Kind CallSiteRuntimeResolver.VisitFactory(abstract TResult VisitFactory)遞迴呼叫 VisitCallSite

相關文章