Net6 DI源碼分析Part4 CallSiteFactory ServiceCallSite
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對象。
-
CallSiteFactory(ICollection
descriptors) - 創建了個堆棧衛士
- copy一份ICollection
給_descriptors - 調用Populate整理出Dictionary<Type, ServiceDescriptorCacheItem> _descriptorLookup
-
Populate()
- 做了一系列驗證並把驗證後的數據添加到Dictionary<Type, ServiceDescriptorCacheItem> _descriptorLookup
-
ServiceCallSite? GetCallSite(Type serviceType, CallSiteChain callSiteChain)
- 先去快取拿_callSiteCache.TryGetValue 如果又直接返回。
- 走CreateCallSite流程
- 會走堆棧衛士
- CreateCallSite會為每個type類型做個鎖
- callSiteChain.CheckCircularDependency(serviceType); 參考CallSiteChain
- 創建ServiceCallSite
- TryCreateExact // 普通的
- TryCreateOpenGeneric// 泛型的
- TryCreateEnumerable // 同一個類型註冊了多個的。
-
ServiceCallSite? GetCallSite(ServiceDescriptor serviceDescriptor, CallSiteChain callSiteChain)
- 此方法是個重載方法,其內部直接調用TryCreateExact(TryCreateExact(ServiceDescriptor descriptor, Type serviceType, CallSiteChain callSiteChain, int slot))與另外一個
GetCallSite
相比,此方法跳過了堆棧衛士,以及鎖,和先從快取_callSiteCache拿的過程。所以速度更快。也僅用於初始化驗證使用,但是TryCreateExact內部調用鏈中也會調用 CreateArgumentCallSites這樣就又使用到第一個重載方法了。也會從新走堆棧衛士等等一系列的操作。算是相對優化。
- 此方法是個重載方法,其內部直接調用TryCreateExact(TryCreateExact(ServiceDescriptor descriptor, Type serviceType, CallSiteChain callSiteChain, int slot))與另外一個
-
ServiceCallSite? TryCreateExact(Type serviceType, CallSiteChain callSiteChain)
- 拿到對應的ServiceDescriptor交給重載方法。
-
ServiceCallSite? TryCreateExact(ServiceDescriptor descriptor, Type serviceType, CallSiteChain callSiteChain, int slot)
-
根據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的創建比較特殊。。
- 調用CreateConstructorCallSite -> CreateArgumentCallSites -> GetCallSite -> 遞歸回TryCreateExact的調用;
-
快取並返回
-
-
CreateConstructorCallSite
準備出一個需要DI「new」出來的一個實例的ConstructorCallSite對象,- 先確認是否有可以用的構造函數。沒有肯定拋錯誤了
- 如果只有一個構造函數,且沒有參數,拿就簡單了直接new 一個ConstructorCallSite返回
- 如果只有一個構造函數,有構造參數,獲取參數列表並調用 CreateArgumentCallSites 且內部遞歸調用回GetCallSite
- 如果有多個構造參數。找出一個合適的構造函數
- 根據構造函數的參數數量做了個降序
Array.Sort(constructors,(a, b) => b.GetParameters().Length.CompareTo(a.GetParameters().Length));
- 參數最多的構造函數
- 校驗其餘構造函數參數內是否有被選構造函數沒有的參數,如果存在沒有的參數就拋異常。
- 根據構造函數的參數數量做了個降序
-
ServiceCallSite[]? CreateArgumentCallSites(
Type implementationType,
CallSiteChain callSiteChain,
ParameterInfo[] parameters,
bool throwIfCallSiteNotFound)- 內部循環遞歸調用GetCallSite(parameterType, callSiteChain);並創建一個ServiceCallSite[]返回。
CallSiteChain
它的出現就是為了防止構造函數形式提供服務時循環依賴項 比如服務A的構造方法依賴服務B,服務B的構造方法又依賴函數A。
以下為兩個關鍵節點。
- CreateCallSite時先確認 CreateCallSite -> callSiteChain.CheckCircularDependency()
- 以構造方法模式創建對象時進行累加。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
//以下沒啥好說的。