[Asp.net 5] DependencyInjection项目代码分析4-微软的实现(3)
这个系列已经写了5篇,链接地址如下:
如果想对本篇有个更好的了解,建议需要先看
“[Asp.net 5] DependencyInjection项目代码分析”
“[Asp.net 5] DependencyInjection项目代码分析4-微软的实现(1)”
“[Asp.net 5] DependencyInjection项目代码分析4-微软的实现(2)”。
在之前的“上“、”中“俩篇已经介绍了ServiceTable、IGenericService、IService、IServiceCallSite、ServiceEntry、GenericService、InstanceService、FactoryService、Service等类。本节主要介绍核心的”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构造函数,[_root=this]。说明新创建的ServiceProvider就是所谓的root。
- 对于使用ServiceProvider为参数的ServiceProvider构造函数。[root=parent._root],说明通过该构造函数构造出的ServiceProvider对象和传入的ServiceProvider对象的_root实际指向同一块地址,并且都是使用 IServiceCollection创建的ServiceProvider对象[需要注意的是正常IServiceCollection.BuildServiceProvider只使用一次,虽然没有定义成全局唯一]。也就是说,使用IServiceCollection.BuildServiceProvider创建的ServiceProvider的作用域是全局的,使用ServiceProvider直接创建的scpoed范围的对象,也会成为全局的对象(没有时机调用其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;
- }
ServiceProvider的属性我们已经讲完,那我们继续看下构造函数内其他的东西(上面的代码就是其构造函数)。我们发现ServiceTable内额外添加IServiceProvider、IServiceScopeFactory、IEnumerable<>的注入。
- IServiceProvider的注入,说明能够创建新的IServiceProvider。我们上面说了只有俩个地方能够创建新的ServiceProvider,并且并不包含ServiceProviderService方法啊。实际上ServiceProviderService是之前介绍的IService, IServiceCallSite接口的实现类,当调用IServiceCallSite的方法” Invoke(ServiceProvider provider)“时,直接返回provider,并没有创建新的ServiceProvider(IServiceProvider)对象,而是使用现有的
- IServiceScopeFactory注入,ServiceScopeService也是实现了IService, IServiceCallSite接口,并且内部会创建ServiceScopeFactory(IServiceScopeFactory)。
- IEnumerable<>的注入,由于篇幅有限,将额外介绍,该处就略过了。
- internal class ServiceProviderService : IService, IServiceCallSite
- {
- public IService Next { get; set; }
- public ServiceLifetime Lifetime
- {
- get { return ServiceLifetime.Scoped; }
- }
- public IServiceCallSite CreateCallSite(ServiceProvider provider, ISet<Type> callSiteChain)
- {
- return this;
- }
- public object Invoke(ServiceProvider provider)
- {
- return provider;
- }
- public Expression Build(Expression provider)
- {
- return provider;
- }
- }
ServiceProviderService
- internal class ServiceScopeService : IService, IServiceCallSite
- {
- public IService Next { get; set; }
- public ServiceLifetime Lifetime
- {
- get { return ServiceLifetime.Scoped; }
- }
- public IServiceCallSite CreateCallSite(ServiceProvider provider, ISet<Type> callSiteChain)
- {
- return this;
- }
- public object Invoke(ServiceProvider provider)
- {
- return new ServiceScopeFactory(provider);
- }
- public Expression Build(Expression provider)
- {
- return Expression.New(
- typeof(ServiceScopeFactory).GetTypeInfo()
- .DeclaredConstructors
- .Single(),
- provider);
- }
- }
- internal class ServiceScopeFactory : IServiceScopeFactory
- {
- private readonly ServiceProvider _provider;
- public ServiceScopeFactory(ServiceProvider provider)
- {
- _provider = provider;
- }
- public IServiceScope CreateScope()
- {
- return new ServiceScope(new ServiceProvider(_provider));
- }
- }
- internal class ServiceScope : IServiceScope
- {
- private readonly ServiceProvider _scopedProvider;
- public ServiceScope(ServiceProvider scopedProvider)
- {
- _scopedProvider = scopedProvider;
- }
- public IServiceProvider ServiceProvider
- {
- get { return _scopedProvider; }
- }
- public void Dispose()
- {
- _scopedProvider.Dispose();
- }
- }
ServiceScopeService、ServiceScopeFactory、ServiceScope
[Asp.net 5] DependencyInjection项目代码分析4-微软的实现(3)的更多相关文章
- [Asp.net 5] DependencyInjection项目代码分析-目录
微软DI文章系列如下所示: [Asp.net 5] DependencyInjection项目代码分析 [Asp.net 5] DependencyInjection项目代码分析2-Autofac [ ...
- [Asp.net 5] DependencyInjection项目代码分析4-微软的实现(5)(IEnumerable<>补充)
Asp.net 5的依赖注入注入系列可以参考链接: [Asp.net 5] DependencyInjection项目代码分析-目录 我们在之前讲微软的实现时,对于OpenIEnumerableSer ...
- [Asp.net 5] DependencyInjection项目代码分析4-微软的实现(4)
这个系列已经写了6篇,链接地址如下: [Asp.net 5] DependencyInjection项目代码分析 [Asp.net 5] DependencyInjection项目代码分析2-Auto ...
- [Asp.net 5] DependencyInjection项目代码分析4-微软的实现(2)
在 DependencyInjection项目代码分析4-微软的实现(1)中介绍了“ServiceTable”.“ServiceEntry”.“IGenericService”.“IService”. ...
- [Asp.net 5] DependencyInjection项目代码分析
最近在研究开源代码,正好发现Asp.net5的源码,下载地址:https://github.com/aspnet. 今天主要讲的是DependencyInjection这部分,抛砖引玉,供大家参考,也 ...
- [Asp.net 5] DependencyInjection项目代码分析2-Autofac
Microsoft.Framework.DependencyInjection.Autofac源码分析 该工程只有一个代码静态类AutofacRegistration,但是该类有3个扩展方法,以及3个 ...
- [Asp.net 5] DependencyInjection项目代码分析3-Ninject
Microsoft.Framework.DependencyInjection.Ninject 该工程内部共包含5个类文件,底层使用Ninject实现依赖注入,工程截图如下: 从文件命名可以看出,Ni ...
- [Asp.net 5] DependencyInjection项目代码分析4-微软的实现(1)
前面俩种实现中,很多内部细节都无法知道,微软的框架也是为了屏蔽具体实现,只让我们关注接口.但是人都是充满好奇的,依赖注入到底是怎么实现的呢? 微软又有怎样的实现呢?下面就为大家一一呈现(说实话,代码真 ...
- Jenkins+Gradle+Sonar进行Java项目代码分析
Jenkins+Maven+Sonar与Jenkins+Gradle+Sonar配置方法很相似,区别就是Java项目所用的编译工具不同,一个是maven,一个是gradle 使用maven编译工具的可 ...
随机推荐
- Syncfusion 复选框 ComboBoxAdv
XAML: <syncfusion:GridTemplateColumn.EditTemplate> <DataTemplate DataType="viewModel:C ...
- JavaScript中需要注意的几个问题
JavaScript是一门弱语言,她使用起来不像C/C++那样有十分繁琐的内存管理.类型定义等,所以学习JavaScript的门槛相对来说也比较低.门槛低并不意味着这门语言很简单,我们在使用的时候会遇 ...
- [.net 面向对象编程基础] (20) LINQ使用
[.net 面向对象编程基础] (20) LINQ使用 通过上节LINQ的基础知识的学习,我们可以开始使用LINQ来进行内存数据的查询了,我们上节说了LINQ的定义为:Language Integr ...
- 在github上写个人简历——最简单却又不容易的内容罗列
前篇博客分享了一下自己初入github的过程,傻瓜式一步步搭建好主页后,终于该做正事儿了——写简历.在脑袋中构思了很多版本,最后终于决定,先写一个最传统的版本,于是我在箱子中翻出我word版本的简历, ...
- 打包上传成功, itunes connect 不出现上传的版本
由于有一次感觉build setting 里 code signing identity设置证书有点乱,有些已经用不到了,就想把那些删除 于是我找到了所有证书的那个文件夹(进入所有证书的那个文件夹), ...
- swift NSComparator
var cmptr:NSComparator = {(obj1:AnyObject!, obj2:AnyObject!) -> NSComparisonResult in if((obj1[&q ...
- 说说设计模式~策略模式(Strategy)
返回目录 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.而对于客户端(UI)来说,可以通过IOC再配合工厂模块,实现动态策略的切换,策略模块通常于一个抽象策略对象(in ...
- 爱上MVC~在Views的多级文件夹~续~分部页的支持
回到目录 之前写的一篇文章,主要针对View视图,它可以放在N级目录下,不必须非要在views/controller/action这种关系了,而在程序运行过程中,发现分页视图对本功能并不支持,原因很简 ...
- Lua字符串库(整理)
Lua字符串库小集 1. 基础字符串函数: 字符串库中有一些函数非常简单,如: 1). string.len(s) 返回字符串s的长度: 2). string.rep(s,n) 返回 ...
- lua如何构造类
function class(super, autoConstructSuper) local classType = {}; classType.autoConstructSuper = autoC ...