·您现在的位置: 云翼网络 >> 文章中心 >> 网站建设 >> 网站建设开发 >> ASP.NET网站开发 >> [Asp.net 5] DependencyInjection项目代码分析4-微软的实现(3)

[Asp.net 5] DependencyInjection项目代码分析4-微软的实现(3)

作者:佚名      ASP.NET网站开发编辑:admin      更新时间:2022-07-23

[asp.net 5] DependencyInjection项目代码分析4-微软的实现(3)

这个系列已经写了5篇,链接地址如下:

[Asp.net 5] DependencyInjection项目代码分析

[Asp.net 5] DependencyInjection项目代码分析2-Autofac

[Asp.net 5] DependencyInjection项目代码分析3-Ninject

[Asp.net 5] DependencyInjection项目代码分析4-微软的实现(1)

[Asp.net 5] DependencyInjection项目代码分析4-微软的实现(2)

如果想对本篇有个更好的了解,建议需要先看

“[Asp.net 5] DependencyInjection项目代码分析”

“[Asp.net 5] DependencyInjection项目代码分析4-微软的实现(1)”

“[Asp.net 5] DependencyInjection项目代码分析4-微软的实现(2)”。

在之前的“上“、”中“俩篇已经介绍了ServiceTable、IGenericService、IService、IServiceCallSiteServiceEntryGenericServiceInstanceService、FactoryServiceService等类。本节主要介绍核心的”ServicePRovider“类。

IServiceProvider类

IServiceProvider类是微软这套DependencyInjection中直接对外的接口。而ServiceProvider是直接实现IServiceProvider并且对外直接提供功能的核心类。

对于ServiceProvider不仅要能够获取注入的类,还需要根据不同定义的范围获取不同范围的注入类。对于不同的范围(Transient、Scoped、Singleton),ServiceProvider需要使用不同的逻辑。我们简单分析下。

  • 对于Transient,每次都创建一个新的实例,所以代码中只需直接创建该注入类的实例即可,不需要对该类进行缓存;
  • 对于Singleton,全局只有一个实例,代码中可以将创建的对象缓存到静态的字典表中,之后每次需要该类型实例首先到全局静态字典表中去查找如果存在则直接返回,不存在创建后加入到字典表中,之后返回。(实际上源码中并未缓存到全局变量中,具体实现方式后面会讲解
  • 对于Scoped,每个Scope内是唯一的,不同Scope范围是不同的。这个就比较难实现。不过我们可以通过外部调用的代码简单的猜测下,之后我们通过查看源代码进行验证。

下面是不同Scoped范围的代码调用。

public void ScopedServiceCanBeResolved()        {            IServiceProvider container = CreateContainer();            var scopeFactory = container.GetService<IServiceScopeFactory>();            using (var scope = scopeFactory.CreateScope())            {                var containerScopedService = container.GetService<IFakeScopedService>();                var scopedService1 = scope.ServiceProvider.GetService<IFakeScopedService>();                var scopedService2 = scope.ServiceProvider.GetService<IFakeScopedService>();                Assert.NotEqual(containerScopedService, scopedService1);                Assert.Equal(scopedService1, scopedService2);            }        }        [Fact]        public void NestedScopedServiceCanBeResolved()        {            IServiceProvider container = CreateContainer();            IServiceScopeFactory outerScopeFactory = container.GetService<IServiceScopeFactory>();            using (var outerScope = outerScopeFactory.CreateScope())            {                var innerScopeFactory = outerScope.ServiceProvider.GetService<IServiceScopeFactory>();                using (var innerScope = innerScopeFactory.CreateScope())                {                    var outerScopedService = outerScope.ServiceProvider.GetService<IFakeScopedService>();                    var innerScopedService = innerScope.ServiceProvider.GetService<IFakeScopedService>();                    Assert.NotEqual(outerScopedService, innerScopedService);                }            }        }
CreateScope

我们可以根据不同Scoped的注入实例,实际上是通过获取不同的IServiceScope对象ServiceProvider属性,之后通过该属性创建。由于是不同的IServiceScope对象,我们可以大胆的假设IServiceScope对象的ServiceProvider属性也是不同的IServiceProvider对象。所以每个ServiceProvider对象内部,只需要维护一份注入对象的副本即可;由于IServiceScope对象实现了IDisposable接口(用在using上的对象,都实现了IDisposable接口,当using范围结束后,会自动调用IDisposable的Dispose方法),但注入的对象缓存在IServiceScope的ServiceProvider属性对象中,所以我们让ServiceProvider类也实现IDisposable接口,在IServiceScope的Dispose方法内部调用ServiceProvider类的Dispose方法即可。

通过上面的分析,我们可以大致想象出ServiceProvider类的定义,下面就是ServiceProvider类缩减的源代码:

internal class ServiceProvider : IServiceProvider, IDisposable    {        private readonly object _sync = new object();        private readonly ServiceProvider _root;        private readonly ServiceTable _table;        private readonly Dictionary<IService, object> _resolvedServices = new Dictionary<IService, object>();        private ConcurrentBag<IDisposable> _disposables = new ConcurrentBag<IDisposable>();        public ServiceProvider(IEnumerable<ServiceDescriptor> serviceDescriptors)        {            _root = this;            _table = new ServiceTable(serviceDescriptors);            _table.Add(typeof(IServiceProvider), new ServiceProviderService());            _table.Add(typeof(IServiceScopeFactory), new ServiceScopeService());            _table.Add(typeof(IEnumerable<>), new OpenIEnumerableService(_table));        }        internal ServiceProvider(ServiceProvider parent)        {            _root = parent._root;            _table = parent._table;        }        public object GetService(Type serviceType);                public void Dispose()        {            var disposables = Interlocked.Exchange(ref _disposables, null);            if (disposables != null)            {                foreach (var disposable in disposables)                {                    disposable.Dispose();                }            }        }}

对于ServiceProvider对象共有5个属性,

  • “_sync”是用于多线程同步的锁对象,我们可以不关心。
  • ServiceTable的“_table”对象,是内部维护注入类关系的字典表。
  • resolvedServices是用于维护在该范围内已经创建好的注入对象,当有该范围内的对象,首先会判断该字典表中是否存在实例,如果存在直接返回,不存在创建新的实例加入到该字典表中,并将该新创建的实例返回。
  • “disposables”是一个多线程支持的列表,从名字就看出这是调用Dispose后需要释放的对象。从Dispose方法也可以认证这点。(当注入的对象是实现了IDisposable接口,就需要在使用结束后调用其Dispose方法将其使用的关键资源释放掉,以免造成内存垃圾和资源浪费。)
  • ServiceProvider类型的“root”属性,这个.....(我估计十有八九会问这是什么鬼?难道ServiceProvider也是链形结构?但是那也不能叫root啊?难道是Singleton的实现,但是为毛不是全局的,而且每个ServiceProvider都有这个副本呢??)。经过参考其简单到鬼的注释,和反复试验发现确实是Singleton的实现。

ServiceProvider的Root属性展开

首先我们刚才没有注意到一个问题,就是ServiceProvider类定义不是public的,而是internal的。来我们回忆下类的定义:

internal class ServiceProvider : IServiceProvider, IDisposable

所以我们在外面是无法调用/构建ServiceProvider的实例的,只能在程序集范围内实例话。那在哪些地方调用该类的构造函数呢?结果发现俩个构造函数都只被调用过一次。下面将调用的代码列出:

public static class ServiceCollectionExtensions    {        public static IServiceProvider BuildServiceProvider(this IServiceCollection services)        {            return new ServiceProvider(services);        }    }internal class ServiceScopeFactory : IServiceScopeFactory    {        private readonly ServiceProvider _provider;        public ServiceScopeFactory(ServiceProvider provider)        {            _provider = provider;        }        public IServiceScope CreateScope()        {            return new ServiceScope(new ServiceProvider(_provider));        }    }
ServiceProvider构造函数
  • 对于ServiceCollectionExtensions类调用的构造函数:这是对于IServiceCollection接口(继承自IList<ServiceDescriptor>)的扩展,实际就是用IServiceCollection对象创建出ServiceProvider对象供外面调用。这是微软实现的依赖注入的入口。我们看使用IServiceCollection为参数的ServiceProvider