Net6 DI源码分析Part4 CallSiteFactory ServiceCallSite
2022/2/9 12:12:32
本文主要是介绍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
//以下没啥好说的。
ConstantCallSite
ConstructorCallSite
FactoryCallSite
IEnumerableCallSite
ServiceProviderCallSite
这篇关于Net6 DI源码分析Part4 CallSiteFactory ServiceCallSite的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23增量更新怎么做?-icode9专业技术文章分享
- 2024-11-23压缩包加密方案有哪些?-icode9专业技术文章分享
- 2024-11-23用shell怎么写一个开机时自动同步远程仓库的代码?-icode9专业技术文章分享
- 2024-11-23webman可以同步自己的仓库吗?-icode9专业技术文章分享
- 2024-11-23在 Webman 中怎么判断是否有某命令进程正在运行?-icode9专业技术文章分享
- 2024-11-23如何重置new Swiper?-icode9专业技术文章分享
- 2024-11-23oss直传有什么好处?-icode9专业技术文章分享
- 2024-11-23如何将oss直传封装成一个组件在其他页面调用时都可以使用?-icode9专业技术文章分享
- 2024-11-23怎么使用laravel 11在代码里获取路由列表?-icode9专业技术文章分享
- 2024-11-22怎么实现ansible playbook 备份代码中命名包含时间戳功能?-icode9专业技术文章分享