Net6 CallSiteFactory ServiceCallSite, CallSiteChain
abstract class ServiceCallSite

ServiceCallSite是个抽象类,实现ConstantCallSite、ConstructorCallSite、 FactoryCallSite、ServiceProviderCallSite、IEnumerableCallSite

ServiceCallSite对一个服务的描述,CallSiteFactory提供了它的构建。engine就是根据此类的描述来进行创建对应的服务。

  1. public abstract Type ServiceType { get; }
  2. public abstract Type? ImplementationType { get; }
  3. public abstract CallSiteKind Kind { get; }
  4. public ResultCache Cache { get; }
  5. public object? Value { get; set; }
  6. 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)用户就涉及到这三种类型。其余的是系统用的。

      1. if (descriptor.ImplementationInstance != null)
      2. {
      3. callSite = new ConstantCallSite(descriptor.ServiceType, descriptor.ImplementationInstance);
      4. }
      5. else if (descriptor.ImplementationFactory != null)
      6. {
      7. callSite = new FactoryCallSite(lifetime, descriptor.ServiceType, descriptor.ImplementationFactory);
      8. }
      9. else if (descriptor.ImplementationType != null)
      10. {
      11. callSite = CreateConstructorCallSite(lifetime, descriptor.ServiceType, descriptor.ImplementationType, callSiteChain);
      12. }

      主要是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. 1. CallSiteFactory.CreateCallSite(serviceType, new callSiteChain()) => callSiteChain.CheckCircularDependency(); => GetCallSite();
  2. GetCallSite => TryCreateExact(ServiceDescriptor descriptor, Type serviceType, CallSiteChain callSiteChain, int slot) => CreateConstructorCallSite
  3. CreateConstructorCallSite => callSiteChain.Add(serviceType, implementationType) ; CreateArgumentCallSites;
  4. 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 的封装。

  1. internal struct ResultCache
  2. {
  3. public ResultCache(ServiceLifetime lifetime, Type type, int slot)
  4. {
  5. switch (lifetime)
  6. {
  7. case ServiceLifetime.Singleton:
  8. Location = CallSiteResultCacheLocation.Root;
  9. break;
  10. case ServiceLifetime.Scoped:
  11. Location = CallSiteResultCacheLocation.Scope;
  12. break;
  13. case ServiceLifetime.Transient:
  14. Location = CallSiteResultCacheLocation.Dispose;
  15. break;
  16. default:
  17. Location = CallSiteResultCacheLocation.None;
  18. break;
  19. }
  20. Key = new ServiceCacheKey(type, slot);
  21. }
  22. public CallSiteResultCacheLocation Location { get; set; }
  23. public ServiceCacheKey Key { get; set; }
  24. }
ServiceCacheKey
  1. 1. CallSiteFacotry根据此类型去缓存CallSiteServie
  2. 2. ServiceProviderEngineScope根据此类型去缓存 ResolvedServices
CallSiteValidator

//以下没啥好说的。

ConstantCallSite
ConstructorCallSite
FactoryCallSite
IEnumerableCallSite
ServiceProviderCallSite

Net6 DI源码分析Part4 CallSiteFactory ServiceCallSite的更多相关文章

  1. Net6 DI源码分析Part5 在Kestrel内Di Scope生命周期是如何根据请求走的?

    Net6 DI源码分析Part5 在Kestrel内Di Scope生命周期是如何根据请求走的? 在asp.net core中的DI生命周期有一个Scoped是根据请求走的,也就是说在处理一次请求时, ...

  2. Net6 DI源码分析Part2 Engine,ServiceProvider

    ServiceProvider ServiceProvider是对IServiceProvider实现,它有一个internal的访问修饰符描述的构造,并需要两个参数IServiceCollectio ...

  3. Net6 DI源码分析Part1 ServiceCollection、ServiceDescriptor、ServiceLifetime、IServiceProvider

    ServiceCollection.ServiceDescriptor.ServiceLifetime.IServiceProvider Microsoft.Extensions.Dependency ...

  4. Net6 DI源码分析Part3 CallSiteRuntimeResolver,CallSiteVisitor

    CallSiteRuntimeResolver CallSiteRuntimeResolver是实现了CallSiteVisitor之一. 提供的方法主要分三个部分 自有成员方法 Resolve提供服 ...

  5. 小白都能看懂的 Spring 源码揭秘之依赖注入(DI)源码分析

    目录 前言 依赖注入的入口方法 依赖注入流程分析 AbstractBeanFactory#getBean AbstractBeanFactory#doGetBean AbstractAutowireC ...

  6. Net6 Configuration & Options 源码分析 Part2 Options

    Net6 Configuration & Options 源码分析 Part2 Options 第二部分主要记录Options 模型 OptionsConfigurationServiceCo ...

  7. 一个由正则表达式引发的血案 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. 一些特殊字符,如“&”,“- ...

  8. Net6 Configuration & Options 源码分析 Part1

    Net6 Configuration & Options 源码分析 Part1 在Net6中配置系统一共由两个部分组成Options 模型与配置系统.它们是两个完全独立的系统. 第一部分主要记 ...

  9. .net core 轻量级容器 ServiceProvider 源码分析

    首先看 ServiceCollection 的定义 //定义 public class ServiceCollection : IServiceCollection { private readonl ...

随机推荐

  1. mysql 语句中 sum函数求和 null 变 0

    https://blog.csdn.net/Z_passionate/article/details/83821039

  2. 【】(Git)用动图展示10大Git命令

    1.说明 git merge.git rebase.git reset.git revert.git fetch.git pull.git reflog-- 你知道这些 git 命令执行的究竟是什么任 ...

  3. hisql 新功能 支持一套sql在不同数据库执行

    目前流行的ORM框架如果需要动态的拼接查询语句,只能用原生的sql进行拼接,无法跨不同数据库执行.hisql推出新的语法一套语句可以在不同的数据库执行 传统ORM框架最大的弊端就是完全要依赖于实体用l ...

  4. python (伪)私有属性和私有方法

    1.定义方式 在定义属性或方法时,在属性名或者方法名前增加两个下划线,定义的就是私有属性或方法. 2.为什么要定义私有属性和私有方法 在实际开发中,对象的某些属性或方法只希望在对象的内部被使用,而不希 ...

  5. 在使用django admin的后台搜索时报错

    在使用django admin的后台搜索时报错 百度说在search_fields中定义了非字符串字段,最后发现author引用了外键 解决办法: 有外健时应写成: 本表外键字段__外键所在表所需要查 ...

  6. [ Flask ] myblog_flask问题集(RESTfull风格)

    VUE问题 前端VUE怎么捕获所有404NOT FOUND的路由呢? [ 解决方案 ] vue-router路由守卫,参考文档:动态路由匹配 对于路由.../edit/<id>,自己能编辑 ...

  7. Oracle 五种约束的创建和移除:

    1.主键约束: 创建表的时候就添加: create table table_name (categoryId varchar2(10), categoryName varchar2(30), prim ...

  8. fis学习

    http://fis.baidu.com/docs/beginning/getting-started.html#md5 还是喜欢时间戳?没问题,FIS也可以满足你的需求,点击这里

  9. x86-3-段式管理(segmentation)

    x86-3-段式管理(segmentation) 3.1 段式管理概述: 从8086CPU开始,为了让程序在内存中能自由浮动而又不影响它的正常执行,CPU将内存划分成逻辑上的段来给程序使用. x86继 ...

  10. Ubuntu18.04 内核升级

    查看当前版本  在终端输入以下命令并回车 uname -sr  可以发现当前内核为 Linux 4.15.0-88-generic 查看目前最新的稳定内核  访问 The Linux Kernel A ...