【橙子老哥】.NetCore IOC依赖注入源码剖析(一)

科技   2024-11-02 15:30   广东  

hello,大家好,今天依然是橙子老哥的分享时间,希望大家一起学习,一起进步。

欢迎加入.net意社区,第一时间了解我们的动态

官方地址:https://ccnetcore.com

微信公众号:搜索 意.Net

添加橙子老哥微信:chengzilaoge520

最近有小伙伴问我,依赖注入天天用,无非就是构造函数递归找构造函数,这有什么难的?可是真正问到细节,却又一知半解,做一个成熟稳定的IOC真的很不容易,需要考虑并发、循环依赖、缓存、泛型、作用域等等因素

今天我们就来看看.NetCore内置的IOC容器,具体源码细节是如何实现的?由于IOC的篇幅较长,橙子老哥带大家从外层一层一层揭开依赖注入的面纱

1、使用

我们需要先安装:Microsoft.Extensions.DependencyInjection包,默认是带了抽象包,没有实现,我们是执行不了BuildServiceProvider的扩展方法的

以下是控制台使用,我们讲IService加入到容器,并在build之后,在从容器中获取出来

using Microsoft.Extensions.DependencyInjection;

var services=newServiceCollection();
services.AddTransient<IService,MyService>();
var serviceProvider=services.BuildServiceProvider();

varmy=serviceProvider.GetRequiredService<IService>();
my.DoWork();

publicinterfaceIService
{
    void DoWork();
}

publicclassMyService:IService
{
    public void DoWork()
    {
        // 实现功能
        Console.WriteLine("你好");
    }
}

以上的使用代码,相信大家早已经了如指掌,同样我这里没必要讲那些基础使用以及概念了

为什么要使用IOC,IOC的生命周期,IOC的好处。。。。。这些留给大家仔细思考吧~

上面的示例的流程,我们分为三步

  1. 1. services.AddTransient<IService,MyService>(); 将服务添加进服务容器中

  2. 2. BuildServiceProvider 构建容器

  3. 3. 通过GetRequiredService从容器中获取到对应的服务

services.AddTransient<IService,MyService>()往里面看看

ServiceDescriptor serviceDescriptor = new ServiceDescriptor(serviceType, implementationType, lifetime);
collection.Add(serviceDescriptor);

这3步主要是用到了3个核心对象

  1. 1. ServiceCollection (服务容器,我们的向容器中添加的服务存储在这里)

  2. 2. ServiceDescriptor (服务描述者,我们向容器中新增的东西会转换成这个)

  3. 3. ServiceProvider(服务提供者,通过服务容器构建之后,我们从这里获取对应的服务)

弄清楚了这个,我们再往里面看

2、ServiceCollection 服务容器

我们看看,这个服务容器里面是如何存储我们新增进去的ServiceDescriptor

  public interface IServiceCollection : IList<ServiceDescriptor>,
    ICollection<ServiceDescriptor>,
    IEnumerable<ServiceDescriptor>,
    IEnumerable
  {
  }

emmm,好像就是一个ServiceDescriptor集合,所以通过扩展方法包几层,ServiceDescriptor通过集合的add方法就新增进去了

真的没有看错,这个玩意儿,本质上就是一个ServiceDescriptor集合

当然,这个集合还做了一步services.BuildServiceProvider();

他是如何进行服务容器的构建的呢?我们往里面看看

public static ServiceProvider BuildServiceProvider(this IServiceCollection services, ServiceProviderOptions options)
{
    if(services isnull)
    {
        throw new ArgumentNullException(nameof(services));
    }
    if(options isnull)
    {
        throw new ArgumentNullException(nameof(options));
    }

    return new ServiceProvider(services, options);
}

BuildServiceProvider 是一个扩展方法,目的是通过IServiceCollection 去创建(new)一个ServiceProvider

3、ServiceProvider 服务提供者-创建

ServiceCollection 真的没有什么内容,现在我们看看ServiceProvider,它主要在我们的流程里面做了两个事情

  1. 1. BuildServiceProvider 的时候去new了一个出来

  2. 2. 调用GetRequiredService 的时候,从服务容器中取出对应的服务

我们先看看它是如何new出来的

private readonly Func<ServiceIdentifier,ServiceAccessor> _createServiceAccessor;

// Internal for testing
internal ServiceProviderEngine _engine;

private bool _disposed;

private readonly ConcurrentDictionary<ServiceIdentifier,ServiceAccessor> _serviceAccessors;

internal CallSiteFactory CallSiteFactory{get;}

internal ServiceProvider EngineScopeRoot{get;}

//构造方法
internal ServiceProvider(ICollection<ServiceDescriptor> serviceDescriptors, ServiceProviderOptions options)
{
// note that Root needs to be set before calling GetEngine(), because the engine may need to access Root
//设置一个根节点的作用域,控制对象生命周期
Root=new ServiceProvider EngineScope(this, isRootScope:true);

//获取到对应的引擎
_engine =GetEngine();

//存储了一个创建服务的委托
 _createServiceAccessor =CreateServiceAccessor;
//对上面的委托以ServiceIdentifier为key 做了一个缓存
_serviceAccessors =new ConcurrentDictionary<ServiceIdentifier,ServiceAccessor>();

//CallSiteFactory 工厂,一个核心对象,用于创建服务
CallSiteFactory=new CallSiteFactory(serviceDescriptors);

// The list of built in services that aren't part of the list of service descriptors
// keep this in sync with CallSiteFactory.IsService
//将固定几个接口加入到CallSiteFactory 工厂中
CallSiteFactory.Add(ServiceIdentifier.FromServiceType(typeof(IServiceProvider)),new ServiceProviderCallSite());
CallSiteFactory.Add(ServiceIdentifier.FromServiceType(typeof(IServiceScopeFactory)),new ConstantCallSite(typeof(IServiceScopeFactory),Root));
CallSiteFactory.Add(ServiceIdentifier.FromServiceType(typeof(IServiceProviderIsService)),new ConstantCallSite(typeof(IServiceProviderIsService),CallSiteFactory));
CallSiteFactory.Add(ServiceIdentifier.FromServiceType(typeof(IServiceProviderIsKeyedService)),new ConstantCallSite(typeof(IServiceProviderIsKeyedService),CallSiteFactory));

//判断是否要校验作用域
if(options.ValidateScopes)
{
     _callSiteValidator =new CallSiteValidator();
}

//是否就在build的时候进行校验循环依赖等问题
if(options.ValidateOnBuild)
{
    List<Exception>? exceptions =null;
    foreach (ServiceDescriptor serviceDescriptor in serviceDescriptors)
    {
        try
        {
            ValidateService(serviceDescriptor);
        }
        catch(Exception e)
        {
            exceptions ??=new List<Exception>();
            exceptions.Add(e);
        }
    }

    if(exceptions !=null)
    {
        throw new AggregateException("Some services are not able to be constructed", exceptions.ToArray());
    }
}

DependencyInjectionEventSource.Log.ServiceProviderBuilt(this);
        }

这里在构造函数中做了很多事情

我们来分析一下

 _engine = GetEngine();

private ServiceProviderEngine GetEngine()
{
    ServiceProviderEngine engine;

#if NETFRAMEWORK || NETSTANDARD2_0
     engine =CreateDynamicEngine();
#else
        if(RuntimeFeature.IsDynamicCodeCompiled&&!DisableDynamicEngine)
        {
                engine =CreateDynamicEngine();
        }
        else
        {
                // Don't try to compile Expressions/IL if they are going to get interpreted
                engine =RuntimeServiceProviderEngine.Instance;
        }
#endif
    return engine;

[UnconditionalSuppressMessage("AotAnalysis""IL3050:RequiresDynamicCode",
                Justification = "CreateDynamicEngine won't be called when using NativeAOT.")
]// see also https://github.com/dotnet/linker/issues/2715
        ServiceProviderEngine CreateDynamicEngine()=>newDynamicServiceProviderEngine(this);
}

这里根据环境的不一样,使用不同的引擎进行构造,这里我们走的是RuntimeServiceProviderEngine.Instance

它是ServiceProviderEngine的派生类,其中只有一个方法RealizeService,引擎用于实现的服务,是一个委托,这里走的是CallSiteRuntimeResolver.Instance,到这里我们先打住一下(后续会对应有核心对象),我们暂时知道,我们去根据环境获取到对应创建服务的引擎,GetEngine,获取到运行时引擎, RuntimeServiceProviderEngine.Instance,这里面又使用CallSiteRuntimeResolver.Instance去创建CallSiteRuntimeResolver类,具体引擎去解析者是通过CallSiteRuntimeResolver完成的

 internal sealedclassRuntimeServiceProviderEngine:ServiceProviderEngine
{
    public static RuntimeServiceProvider EngineInstance{get;}=new RuntimeServiceProviderEngine();

    private RuntimeServiceProviderEngine()
    {
    }

    public override Func<ServiceProviderEngineScope,object?>RealizeService(
ServiceCallSite callSite)
    {
        return (Func<ServiceProviderEngineScope,object>)(scope =>CallSiteRuntimeResolver.Instance.Resolve(callSite, scope));
    }
}


internal sealed class CallSiteRuntimeResolver:CallSiteVisitor<RuntimeResolverContext,object?>
{
    public static CallSiteRuntimeResolverInstance{get;}=new CallSiteRuntimeResolver();
 }

我们回到ServiceProvider 继续看,CreateServiceAccessor这个里面做了什么

private ServiceAccessor CreateServiceAccessor(ServiceIdentifier serviceIdentifier)
{
    //通过CallSiteFactory 通过服务的身份证去获取对应是CallSite
    ServiceCallSite? callSite =CallSiteFactory.GetCallSite(serviceIdentifier,newCallSiteChain());
    if(callSite !=null)
    {
        DependencyInjectionEventSource.Log.CallSiteBuilt(this, serviceIdentifier.ServiceType, callSite);
        OnCreate(callSite);

//如果通过CallSiteFactory获取到对应的CallSite,一口气塞给引擎去处理
        Func<ServiceProviderEngineScope,object?> realizedService = _engine.RealizeService(callSite);
        return new ServiceAccessor{CallSite= callSite,RealizedService= realizedService };
    }
    return new ServiceAccessor{CallSite= callSite,RealizedService= _ =>null};
}

这里我们注意两个核心对象,CallSiteFactory (获取对应的ServiceCallSite)和ServiceProviderEngine (上面的容器引擎) 这个容器引擎,刚好又回到最前面的容器引擎的RealizeService实现方法,接到上面的引擎最后执行的是CallSiteRuntimeResolver 运行时CallSite解析者

这里透露着两个信息,我们一定要清楚,不然很容易被这些核心对象给转晕

  1. 1. CallSiteFactory callsite工厂是为了创建ServiceCallSite

  2. 2. ServiceCallSite 是用于存储服务访问配置,通过它进行封装所需要实例化的信息

  3. 3. 有了ServiceCallSite 之后,再将它交给对应的引擎去解析

OK,到这里又会发现,多了很多概念和很多核心对象,之后我们会一一介绍,我们再回到构建的时候

这里还做了最后一步

if (options.ValidateOnBuild)
{
    ValidateService(serviceDescriptor);
}

如果我们在构建服务容器的时候,传入了option,它还会去校验循环依赖

var serviceProvider=services.BuildServiceProvider(new ServiceProviderOptions(){ValidateOnBuild=true});

//如果开启了ValidateOnBuild ,就执行下面的代码
private void ValidateService(ServiceDescriptor descriptor)
{
    if(descriptor.ServiceType.IsGenericType&&!descriptor.ServiceType.IsConstructedGenericType)
    {
        return;
    }

    try
    {
        ServiceCallSite? callSite =CallSiteFactory.GetCallSite(descriptor,newCallSiteChain());
        if(callSite !=null)
        {
            OnCreate(callSite);
        }
    }
    catch(Exception e)
    {
        throw new InvalidOperationException($"Error while validating the service descriptor '{descriptor}': {e.Message}", e);
    }
}

我们先去遍历了容器中所有的ServiceDescriptor ,然后通过CallSiteFactory去获取对应的服务

这里使用一个try包住了 ServiceCallSite? callSite = CallSiteFactory.GetCallSite(descriptor, new CallSiteChain());方法,如果CallSiteFactory内部获取不到报错了,那直接抛出异常

这个 CallSiteFactory 到底是何方神圣,好像获取服务相关,每次都有它,我们后续在玩一玩它

4、ServiceProvider 服务提供者-获取服务

GetRequiredService这里也是扩展方法,一层包一层的,最终,走到以下内容

internal object? GetService(ServiceIdentifier serviceIdentifier,ServiceProviderEngineScope serviceProviderEngineScope)
{
    if(_disposed)
    {
        ThrowHelper.ThrowObjectDisposedException();
    }
    ServiceAccessor serviceAccessor = _serviceAccessors.GetOrAdd(serviceIdentifier, _createServiceAccessor);
    OnResolve(serviceAccessor.CallSite, serviceProviderEngineScope);
    DependencyInjectionEventSource.Log.ServiceResolved(this, serviceIdentifier.ServiceType);
    object? result = serviceAccessor.RealizedService?.Invoke(serviceProviderEngineScope);
    System.Diagnostics.Debug.Assert(result isnull||CallSiteFactory.IsService(serviceIdentifier));
    return result;
}

_serviceAccessors 前面我们说过,它只是一个 private readonly ConcurrentDictionary<ServiceIdentifier, ServiceAccessor> _serviceAccessors;线程安全字典,用于将serviceIdentifier服务身份证作为key,进行缓存我们的CreateServiceAccessor方法

CreateServiceAccessor是用于创建对应的服务的过程,用ServiceIdentifier进行缓存,每种ServiceIdentifier对应一种创建服务的过程

这里只是执行了一步serviceAccessor.RealizedService?.Invoke(serviceProviderEngineScope);,通过serviceAccessor和引擎去解析ServiceIdentifier中的服务

这里又一个核心对象,ServiceAccessor

private sealedclassServiceAccessor
{
    //存储一个服务配置Callsite
    public ServiceCallSite? CallSite {get;set;}
    //存储一个通过Root服务提供者引擎解析到服务实例的委托
    public Func<ServiceProviderEngineScope,object?>? RealizedService {get;set;}
}

这里我们在构造函数的时候,就已经给它赋值了

//ServiceProvider 构建的时候
//存储了一个创建服务的委托

_createServiceAccessor = CreateServiceAccessor;


private ServiceAccessor CreateServiceAccessor(ServiceIdentifier serviceIdentifier)
{
    ServiceCallSite? callSite =CallSiteFactory.GetCallSite(serviceIdentifier,newCallSiteChain());
    if(callSite !=null)
    {
        DependencyInjectionEventSource.Log.CallSiteBuilt(this, serviceIdentifier.ServiceType, callSite);
        OnCreate(callSite);

        // Optimize singleton case
         if(callSite.Cache.Location==CallSiteResultCacheLocation.Root)
        {
            objectvalue=CallSiteRuntimeResolver.Instance.Resolve(callSite,Root);
            return new ServiceAccessor{CallSite= callSite,RealizedService= scope =>value};
        }

        Func<ServiceProviderEngineScope,object?> realizedService = _engine.RealizeService(callSite);
        return new ServiceAccessor{CallSite= callSite,RealizedService= realizedService };
       }
    returnnewServiceAccessor{CallSite= callSite,RealizedService= _ =>null};
}

好家伙,兜兜转转又回来了这里,又是这个CallSiteFactory

这回晚上睡觉掀开被窝一看,嚯!又是你CallSiteFactory

至此,我们只是走完了最外一层,这些大致流程的是清楚的,只是留下很多核心对象没有进行深入,但是也不会太妨碍我们阅读,这其实也是精妙之处,微软将构建引擎、CallSite等对象抽象出来,不着急,关注橙子老哥,一步一步带你深入 (我其实不太喜欢将文章拆分很多个,奈何微软的IOC相对于其他组件来说,难度确实高了很多,所以篇幅有限,也为了让大家更好理解,特意将后续内容拆分多个章节)


.Net意社区,高频发布原创有深度的.Net相关知识内容

与你一起学习,一起进步

DotNet NB
.NET 技术学习分享,社区热点分享,专注为 .NET 社区做贡献,愿我们互相交流学习,共同推动社区发展
 最新文章