Net6 DI原始碼分析Part4 CallSiteFactory ServiceCallSite

一身大膘發表於2022-02-11
Net6 CallSiteFactory ServiceCallSite, CallSiteChain
abstract class ServiceCallSite

ServiceCallSite是個抽象類,實現ConstantCallSite、ConstructorCallSite、 FactoryCallSite、ServiceProviderCallSite、IEnumerableCallSite
ServiceCallSite對一個服務的描述,CallSiteFactory提供了它的構建。engine就是根據此類的描述來進行建立對應的服務。

public abstract Type ServiceType { get; }
public abstract Type? ImplementationType { get; }
public abstract CallSiteKind Kind { get; }
public ResultCache Cache { get; }
public object? Value { get; set; }
public bool CaptureDisposable =>
CallSiteFactory

可以說整個DI中它最忙了。它的作用是為engine提供一個描述服務資訊的ServiceCallSite物件。

  1. CallSiteFactory(ICollection descriptors)

    1. 建立了個堆疊衛士
    2. copy一份ICollection給_descriptors
    3. 呼叫Populate整理出Dictionary<Type, ServiceDescriptorCacheItem> _descriptorLookup
  2. Populate()

    1. 做了一系列驗證並把驗證後的資料新增到Dictionary<Type, ServiceDescriptorCacheItem> _descriptorLookup
  3. ServiceCallSite? GetCallSite(Type serviceType, CallSiteChain callSiteChain)

    1. 先去快取拿_callSiteCache.TryGetValue 如果又直接返回。
    2. 走CreateCallSite流程
      1. 會走堆疊衛士
      2. CreateCallSite會為每個type型別做個鎖
      3. callSiteChain.CheckCircularDependency(serviceType); 參考CallSiteChain
      4. 建立ServiceCallSite
        1. TryCreateExact // 普通的
        2. TryCreateOpenGeneric// 泛型的
        3. TryCreateEnumerable // 同一個型別註冊了多個的。
  4. ServiceCallSite? GetCallSite(ServiceDescriptor serviceDescriptor, CallSiteChain callSiteChain)

    1. 此方法是個過載方法,其內部直接呼叫TryCreateExact(TryCreateExact(ServiceDescriptor descriptor, Type serviceType, CallSiteChain callSiteChain, int slot))與另外一個GetCallSite相比,此方法跳過了堆疊衛士,以及鎖,和先從快取_callSiteCache拿的過程。所以速度更快。也僅用於初始化驗證使用,但是TryCreateExact內部呼叫鏈中也會呼叫 CreateArgumentCallSites這樣就又使用到第一個過載方法了。也會從新走堆疊衛士等等一系列的操作。算是相對優化。
  5. ServiceCallSite? TryCreateExact(Type serviceType, CallSiteChain callSiteChain)

    1. 拿到對應的ServiceDescriptor交給過載方法。
  6. ServiceCallSite? TryCreateExact(ServiceDescriptor descriptor, Type serviceType, CallSiteChain callSiteChain, int slot)

    1. 根據ServiceDescriptor建立CallSite(ConstantCallSite/ImplementationFactory/ ConstructorCallSite)使用者就涉及到這三種型別。其餘的是系統用的。

        if (descriptor.ImplementationInstance != null)
        {
            callSite = new ConstantCallSite(descriptor.ServiceType, descriptor.ImplementationInstance);
        }
        else if (descriptor.ImplementationFactory != null)
        {
            callSite = new FactoryCallSite(lifetime, descriptor.ServiceType, descriptor.ImplementationFactory);
        }
        else if (descriptor.ImplementationType != null)
        {
            callSite = CreateConstructorCallSite(lifetime, descriptor.ServiceType, descriptor.ImplementationType, callSiteChain);
        }
      

      主要是CreateConstructorCallSite的建立比較特殊。。

      1. 呼叫CreateConstructorCallSite -> CreateArgumentCallSites -> GetCallSite -> 遞迴回TryCreateExact的呼叫;
    2. 快取並返回

  7. CreateConstructorCallSite
    準備出一個需要DI“new”出來的一個例項的ConstructorCallSite物件,

    1. 先確認是否有可以用的建構函式。沒有肯定拋錯誤了
    2. 如果只有一個建構函式,且沒有引數,拿就簡單了直接new 一個ConstructorCallSite返回
    3. 如果只有一個建構函式,有構造引數,獲取引數列表並呼叫 CreateArgumentCallSites 且內部遞迴呼叫回GetCallSite
    4. 如果有多個構造引數。找出一個合適的建構函式
      1. 根據建構函式的引數數量做了個降序Array.Sort(constructors,(a, b) => b.GetParameters().Length.CompareTo(a.GetParameters().Length));
      2. 引數最多的建構函式
      3. 校驗其餘建構函式引數內是否有被選建構函式沒有的引數,如果存在沒有的引數就拋異常。
  8. ServiceCallSite[]? CreateArgumentCallSites(
    Type implementationType,
    CallSiteChain callSiteChain,
    ParameterInfo[] parameters,
    bool throwIfCallSiteNotFound)

    1. 內部迴圈遞迴呼叫GetCallSite(parameterType, callSiteChain);並建立一個ServiceCallSite[]返回。
CallSiteChain

它的出現就是為了防止建構函式形式提供服務時迴圈依賴項 比如服務A的構造方法依賴服務B,服務B的構造方法又依賴函式A。
以下為兩個關鍵節點。

  1. CreateCallSite時先確認 CreateCallSite -> callSiteChain.CheckCircularDependency()
  2. 以構造方法模式建立物件時進行累加。CreateConstructorCallSite => callSiteChain.Add(serviceType, implementationType)

虛擬碼呼叫過著如下

    1. CallSiteFactory.CreateCallSite(serviceType, new callSiteChain()) => callSiteChain.CheckCircularDependency(); => GetCallSite();
           GetCallSite => TryCreateExact(ServiceDescriptor descriptor, Type serviceType, CallSiteChain callSiteChain, int slot) => CreateConstructorCallSite
                 CreateConstructorCallSite => callSiteChain.Add(serviceType, implementationType) ; CreateArgumentCallSites;
                    CreateArgumentCallSites => 遞迴回GetCallSite();

CallSiteKind

描述CallSiteService類別的列舉
Factory,Constructor,Constant,IEnumerable,ServiceProvider,
在CallSiteVisitor.VisitCallSiteMain根據此列舉去呼叫對應建立例項的Visit方法 如VisitFactory/VisitIEnumerable/VisitConstructor/VisitConstant/ VisitServiceProvider/

CallSiteResultCacheLocation

描述建立好的服務快取位子,也對應著服務註冊時的生命週期。。
在CallSiteVisitor->VisitCallSite根據此列舉去呼叫不同的VisitCache 如VisitRootCache/VisitScopeCache
switch (lifetime) { case ServiceLifetime.Singleton: Location = CallSiteResultCacheLocation.Root; break; case ServiceLifetime.Scoped: Location = CallSiteResultCacheLocation.Scope; break; case ServiceLifetime.Transient: Location = CallSiteResultCacheLocation.Dispose; break; default: Location = CallSiteResultCacheLocation.None; break; }

ResultCache

用來描述CallSiteService 對ServiceCacheKey/CallSiteResultCacheLocation 的封裝。

internal struct ResultCache
    {
        public ResultCache(ServiceLifetime lifetime, Type type, int slot)
        {
            switch (lifetime)
            {
                case ServiceLifetime.Singleton:
                    Location = CallSiteResultCacheLocation.Root;
                    break;
                case ServiceLifetime.Scoped:
                    Location = CallSiteResultCacheLocation.Scope;
                    break;
                case ServiceLifetime.Transient:
                    Location = CallSiteResultCacheLocation.Dispose;
                    break;
                default:
                    Location = CallSiteResultCacheLocation.None;
                    break;
            }
            Key = new ServiceCacheKey(type, slot);
        }
 
        public CallSiteResultCacheLocation Location { get; set; }
 
        public ServiceCacheKey Key { get; set; }
    }
ServiceCacheKey
1. CallSiteFacotry根據此型別去快取CallSiteServie。
2. ServiceProviderEngineScope根據此型別去快取 ResolvedServices
CallSiteValidator

//以下沒啥好說的。

ConstantCallSite
ConstructorCallSite
FactoryCallSite
IEnumerableCallSite
ServiceProviderCallSite

相關文章