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的更多相关文章
- Net6 DI源码分析Part5 在Kestrel内Di Scope生命周期是如何根据请求走的?
Net6 DI源码分析Part5 在Kestrel内Di Scope生命周期是如何根据请求走的? 在asp.net core中的DI生命周期有一个Scoped是根据请求走的,也就是说在处理一次请求时, ...
- Net6 DI源码分析Part2 Engine,ServiceProvider
ServiceProvider ServiceProvider是对IServiceProvider实现,它有一个internal的访问修饰符描述的构造,并需要两个参数IServiceCollectio ...
- Net6 DI源码分析Part1 ServiceCollection、ServiceDescriptor、ServiceLifetime、IServiceProvider
ServiceCollection.ServiceDescriptor.ServiceLifetime.IServiceProvider Microsoft.Extensions.Dependency ...
- Net6 DI源码分析Part3 CallSiteRuntimeResolver,CallSiteVisitor
CallSiteRuntimeResolver CallSiteRuntimeResolver是实现了CallSiteVisitor之一. 提供的方法主要分三个部分 自有成员方法 Resolve提供服 ...
- 小白都能看懂的 Spring 源码揭秘之依赖注入(DI)源码分析
目录 前言 依赖注入的入口方法 依赖注入流程分析 AbstractBeanFactory#getBean AbstractBeanFactory#doGetBean AbstractAutowireC ...
- Net6 Configuration & Options 源码分析 Part2 Options
Net6 Configuration & Options 源码分析 Part2 Options 第二部分主要记录Options 模型 OptionsConfigurationServiceCo ...
- 一个由正则表达式引发的血案 vs2017使用rdlc实现批量打印 vs2017使用rdlc [asp.net core 源码分析] 01 - Session SignalR sql for xml path用法 MemCahe C# 操作Excel图形——绘制、读取、隐藏、删除图形 IOC,DIP,DI,IoC容器
1. 血案由来 近期我在为Lazada卖家中心做一个自助注册的项目,其中的shop name校验规则较为复杂,要求:1. 英文字母大小写2. 数字3. 越南文4. 一些特殊字符,如“&”,“- ...
- Net6 Configuration & Options 源码分析 Part1
Net6 Configuration & Options 源码分析 Part1 在Net6中配置系统一共由两个部分组成Options 模型与配置系统.它们是两个完全独立的系统. 第一部分主要记 ...
- .net core 轻量级容器 ServiceProvider 源码分析
首先看 ServiceCollection 的定义 //定义 public class ServiceCollection : IServiceCollection { private readonl ...
随机推荐
- Ditto剪贴板增强工具
1.简介 Ditto是一款强大的Windows剪贴板增强工具,它支持64位操作系统,而且完全免费,绿色开源,支持中文,而且还有免安装的绿色版本. 开启Ditto后,不会有任何程序界面出现,它只是默默地 ...
- springboot 项目在idea 中不能起动,但是在eclipse中能起动
新建的springboot 项目,在idea中用main方法起动时出现如下 : 但是把项目导入到eclispe中却能正常运行,百思不其解,网上一通百度,有的说没有依赖springboot的web 启动 ...
- Go的WaitGroup源码分析
WaitGroup 是开发中经常用到的并发控制手段,其源代码在 src/sync/waitgroup.go 文件中,定义了 1 个结构体和 4 个方法: WaitGroup{}:结构体. state( ...
- IDEA开启热部署
双击shift,查找Registry
- 蓝桥杯ALGO-1003
问题描述 JiaoShou在爱琳大陆的旅行完毕,即将回家,为了纪念这次旅行,他决定带回一些礼物给好朋友. 在走出了怪物森林以后,JiaoShou看到了排成一排的N个石子. 这些石子很漂亮,JiaoSh ...
- JAVA8-STREAM 使用说明
概述 本人在java开发过程中,有些知识点需要记录整理,我尽量严谨的叙述我学习的经过和心得,以便备份和和大家一起进步学习,此篇文章是在网上多出搜集整理验证,结尾会注明出处,今天学习一个java8新的功 ...
- spring-data-jpa ----OneToMany 一对多
环境搭建 导入依赖 maven3.6.3 <properties> <spring.version>5.2.5.RELEASE</spring.version> ...
- PHP代码审计之create_function()函数
0x00 create_function()简介 适用范围:PHP 4> = 4.0.1,PHP 5,PHP 7 功能:根据传递的参数创建匿名函数,并为其返回唯一名称. 语法: 1 create ...
- Sentry 企业级数据安全解决方案 - Relay PII 和数据清理
本文档描述了一种我们希望最终对用户隐藏的配置格式.该页面仍然存在的唯一原因是当前 Relay 接受这种格式以替代常规数据清理设置. 以下文档探讨了 Relay 使用和执行的高级数据清理配置的语法和语义 ...
- 【pwn】学pwn日记——栈学习(持续更新)
[pwn]学pwn日记--栈学习(持续更新) 前言 从8.2开始系统性学习pwn,在此之前,学习了部分汇编指令以及32位c语言程序的堆栈图及函数调用. 学习视频链接:XMCVE 2020 CTF Pw ...