.net core IOC 容器实现(四) -- CallSiteRuntimeResolver
上一节聊了一下 CallSite 是怎样生成的,这一节我们来看一下 CallSite 是如何使用的。入口
先让我们来回顾一下CreateServiceAccessor 这个方法。
private Func<ServiceProviderEngineScope, object?> CreateServiceAccessor(Type serviceType)
{
//通过 服务类型 获取 callSite
ServiceCallSite? callSite = CallSiteFactory.GetCallSite(serviceType, new CallSiteChain());
if (callSite != null)
{
if (callSite.Cache.Location == CallSiteResultCacheLocation.Root)
{
//直接解析
object? value = CallSiteRuntimeResolver.Instance.Resolve(callSite, Root);
}
return _engine.RealizeService(callSite);
}
}这段代码跟 CallSite 有关的一共有三个地方,分别是 GetCallSite 和 Resolve(callSite,Root)以及 _engine.RealizeService。其中 GetCallSite 是用来生成 CallSite 的(也就是上一节的主要内容),而剩下的两个则是对于 CallSite 的使用,也是这一节的主要内容。
RealizeService
我们先看一下 _engine.RealizeService 方法。
public override Func<ServiceProviderEngineScope, object?> RealizeService(ServiceCallSite callSite)
{
return scope =>
{
var result = CallSiteRuntimeResolver.Instance.Resolve(callSite, scope);
return result;
};
}我们可以发现最终调用的还是 CallSiteRuntimeResolver.Instance.Resolve 这个方法,所以其实归根结底对 CallSite 的调用其实最终就是一个地方,也就是这个 Resolve 方法。
CallSiteRuntimeResolver.Resolve
public object? Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
{
// 如果在 root scope 范围里已经有缓存了,直接返回
if (scope.IsRootScope && callSite.Value is object cached)
{
return cached;
}
//调用 VisitCallSite 进行解析
return VisitCallSite(callSite, new RuntimeResolverContext
{
Scope = scope
});
}VisitCallSite
protected virtual TResult VisitCallSite(ServiceCallSite callSite, TArgument argument)
{
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();
}
}VisitCallSite 会根据 Location 进行分支处理,Location 是 CallSite 里的一个属性。其中 Root 对应 Singleton,Scope 对应 Scope 生命周期,Dispose 对应 Trasient,None可以先将其理解为Singleton。
VisitRootCache(Single)
protected override object? VisitRootCache(ServiceCallSite callSite, RuntimeResolverContext context)
{
// Singleton 懒加载 如果有 value 直接返回
if (callSite.Value is object value)
{
// Value already calculated, return it directly
return value;
}
var lockType = RuntimeResolverLock.Root;
//在 root 范围进行解析
ServiceProviderEngineScope serviceProviderEngine = context.Scope.RootProvider.Root;
// 锁住 callSite 防止重复生成value
lock (callSite)
{
// Lock the callsite and check if another thread already cached the value
// 可能其他地方已经生成了,在获取一下看看
if (callSite.Value is object callSiteValue)
{
return callSiteValue;
}
//最终依旧是调用了 VisitCallSiteMain 方法
object? resolved = VisitCallSiteMain(callSite, new RuntimeResolverContext
{
Scope = serviceProviderEngine,
AcquiredLocks = context.AcquiredLocks | lockType
});
serviceProviderEngine.CaptureDisposable(resolved);
//进行缓存
callSite.Value = resolved;
return resolved;
}
}VisitScopeCache(Scope)
// Check if we are in the situation where scoped service was promoted to singleton
// and we need to lock the root
// Scope 依赖 Singleton
return context.Scope.IsRootScope ?
VisitRootCache(callSite, context) :
VisitCache(callSite, context, context.Scope, RuntimeResolverLock.Scope);private object? VisitCache(
ServiceCallSite callSite,
RuntimeResolverContext context,
ServiceProviderEngineScope serviceProviderEngine,
RuntimeResolverLock lockType)
{
bool lockTaken = false;
object sync = serviceProviderEngine.Sync;
// Dictionary<ServiceCacheKey, object?> ResolvedServices { get; } 缓存
Dictionary<ServiceCacheKey, object?> resolvedServices = serviceProviderEngine.ResolvedServices;
// Taking locks only once allows us to fork resolution process
// on another thread without causing the deadlock because we
// always know that we are going to wait the other thread to finish before
// releasing the lock
// 锁住 sync 对象
if ((context.AcquiredLocks & lockType) == 0)
{
Monitor.Enter(sync, ref lockTaken);
}
try
{
//获取锁之后
// Note: This method has already taken lock by the caller for resolution and access synchronization.
// For scoped: takes a dictionary as both a resolution lock and a dictionary access lock.
//先访问缓存
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;
}
finally
{
//解锁
if (lockTaken)
{
Monitor.Exit(sync);
}
}
}VisitDisposeCache(Transient)
protected override object? VisitDisposeCache(ServiceCallSite transientCallSite, RuntimeResolverContext context)
{
//解析
var instance=VisitCallSiteMain(transientCallSite, context);
return context.Scope.CaptureDisposable(instance);
}VisitNoCache(None)
protected virtual TResult VisitNoCache(ServiceCallSite callSite, TArgument argument)
{
return VisitCallSiteMain(callSite, argument);
}VisitCallSiteMain
观察以上方法,我们可以发现无论是 VisitRootCache还是VisitScopeCache 等等,最终都是调用 VisitCallSiteMain 这个方法来生成的实例。
protected virtual TResult VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
{
//callSite.kind 是只读属性 ,在 GetCallSite 时确定,根据 CallSite 类型确定(例 ConstantCallSite)
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()));
}
}VisitConstructor
其中比较复杂的就是这个 VisitConstructor 方法,通过反射来构造实例,主要思路是拿到实例类型的构造函数,然后通过递归(调用VisitCallSite(见本文最上方))准备构造函数所需要的参数,最后调用 invoke 来生成实例。
protected override object VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
{
object?[] parameterValues;
//获取构造需要使用的参数
//无参
if (constructorCallSite.ParameterCallSites.Length == 0)
{
parameterValues = Array.Empty<object>();
}
//有参
else
{
parameterValues = new object?;
for (int index = 0; index < parameterValues.Length; index++)
{
//递归解析 VisitCallSite 见上文
parameterValues = VisitCallSite(constructorCallSite.ParameterCallSites, context);
}
}
#if NETFRAMEWORK || NETSTANDARD2_0
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;
}
#else
return constructorCallSite.ConstructorInfo.Invoke(BindingFlags.DoNotWrapExceptions, binder: null,
parameters: parameterValues, culture: null);
#endif
}VisitFactory
protected override object VisitFactory(FactoryCallSite factoryCallSite, RuntimeResolverContext context)
{
//调用Factory(Func<IServiceProvider, object> Factory) 委托(委托由开发者实现)
return factoryCallSite.Factory(context.Scope);
}VisitIEnumerable
protected override object VisitIEnumerable(IEnumerableCallSite enumerableCallSite, RuntimeResolverContext context)
{
//创建枚举类型的数组
var array = Array.CreateInstance(
enumerableCallSite.ItemType,
enumerableCallSite.ServiceCallSites.Length);
//给数组填充值
for (int index = 0; index < enumerableCallSite.ServiceCallSites.Length; index++)
{
object? value = VisitCallSite(enumerableCallSite.ServiceCallSites, context);
array.SetValue(value, index);
}
return array;
}VisitConstant
protected override object? VisitConstant(ConstantCallSite constantCallSite, RuntimeResolverContext context)
{
//直接返回保存的实例
return constantCallSite.DefaultValue;
}VisitServiceProvider
protected override object VisitServiceProvider(ServiceProviderCallSite serviceProviderCallSite, RuntimeResolverContext context)
{
return context.Scope;
}总结
先根据实例的生命周期进行分支判断,接下来根据服务的生成方式进行分支判断。
来源:https://www.cnblogs.com/ccwzl/archive/2023/07/24/17512617.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!
页:
[1]