在 DependencyInjection项目代码分析4-微软的实现(1)中介绍了“ServiceTable”、“ServiceEntry”、“IGenericService”、“IService”、“IServiceCallSite”,这篇介绍下“IGenericService、"IService"、"IServiceCallSite"实现类

GenericService

做为IGenericService的唯一实现类,该类言简意赅,请看代码:

internal class GenericService : IGenericService{private readonly ServiceDescriptor _descriptor;public GenericService(ServiceDescriptor descriptor){_descriptor = descriptor;}public ServiceLifetime Lifetime{get { return _descriptor.Lifetime; }}public IService GetService(Type closedServiceType){Type[] genericArguments = closedServiceType.GetTypeInfo().GenericTypeArguments;Type closedImplementationType =_descriptor.ImplementationType.MakeGenericType(genericArguments);var closedServiceDescriptor = new ServiceDescriptor(closedServiceType, closedImplementationType, Lifetime);return new Service(closedServiceDescriptor);}}

GenericService

[var b=typeof(List<B>).GetTypeInfo().GenericTypeArguments会返回{Type(B)}(也就是包含B类型的数组);

typeof(List<>).MakeGenericType(b) 会返回List<B>类型]

所以GenericService的GetService方法的入参是已经“实参化”的泛型类型,类似于IList<B>,而内部的_descriptor.ImplementationType类型则是非“实参化”的泛型,类似于List<>,所以该方法会在一个类似于[Service(IList<B>,List<B>)]的Service。

InstanceService

这个类实现IService, IServiceCallSite俩个接口。直接将已经有的实例作为注入的类型。

internal class InstanceService : IService, IServiceCallSite{private readonly ServiceDescriptor _descriptor;public InstanceService(ServiceDescriptor descriptor){_descriptor = descriptor;}public IService Next { get; set; }public ServiceLifetime Lifetime{get { return _descriptor.Lifetime; }}public IServiceCallSite CreateCallSite(ServiceProvider provider, ISet<Type> callSiteChain){return this;}public object Invoke(ServiceProvider provider){return _descriptor.ImplementationInstance;}public Expression Build(Expression provider){return Expression.Constant(_descriptor.ImplementationInstance, _descriptor.ServiceType);}}

InstanceService

FactoryService

这个类也实现IService, IServiceCallSite俩个接口。直接将已经有的工厂作为注入的类型。

internal class FactoryService : IService, IServiceCallSite{private readonly ServiceDescriptor _descriptor;public FactoryService(ServiceDescriptor descriptor){_descriptor = descriptor;}public IService Next { get; set; }public ServiceLifetime Lifetime{get { return _descriptor.Lifetime; }}public IServiceCallSite CreateCallSite(ServiceProvider provider, ISet<Type> callSiteChain){return this;}public object Invoke(ServiceProvider provider){return _descriptor.ImplementationFactory(provider);}public Expression Build(Expression provider){Expression<Func<IServiceProvider, object>> factory =serviceProvider => _descriptor.ImplementationFactory(serviceProvider);return Expression.Invoke(factory, provider);}}

FactoryService

Service

InstanceService、FactoryService不同Service只实现IService接口,而Service内部包含三个实现IServiceCallSite的内部类,[上一篇中,有关于IService和IServiceCallSite为什么不合并成同一个接口,此处就有了答案,将代码彻底的解耦;当然多个接口设计可能是当初就是这么设计,也可能进一步重构修改成这样的]。

先看下程序的主要架构代码:

 internal class Service : IService{private readonly ServiceDescriptor _descriptor;public Service(ServiceDescriptor descriptor){_descriptor = descriptor;}public IService Next { get; set; }public ServiceLifetime Lifetime{get { return _descriptor.Lifetime; }}public IServiceCallSite CreateCallSite(ServiceProvider provider, ISet<Type> callSiteChain);
}

现在主要问题就围绕着方法( IServiceCallSite CreateCallSite(ServiceProvider provider, ISet<Type> callSiteChain))展开了。

我们先回想下ServiceTable类的构造函数

public ServiceTable(IEnumerable<ServiceDescriptor> descriptors){_services = new Dictionary<Type, ServiceEntry>();_genericServices = new Dictionary<Type, List<IGenericService>>();foreach (var descriptor in descriptors){var serviceTypeInfo = descriptor.ServiceType.GetTypeInfo();if (serviceTypeInfo.IsGenericTypeDefinition){Add(descriptor.ServiceType, new GenericService(descriptor));}else if (descriptor.ImplementationInstance != null){Add(descriptor.ServiceType, new InstanceService(descriptor));}else if (descriptor.ImplementationFactory != null){Add(descriptor.ServiceType, new FactoryService(descriptor));}else{Add(descriptor.ServiceType, new Service(descriptor));}}}

ServiceTable构造函数

对于ServiceDescriptor对象,只有提供ImplementationInstance和ImplementationFactory都没有提供的时候,也就是只提供ImplementationType才会创建Service类。所以在Service类内部想创建对象的实例,只有通过反射创建对象这一条路可走了。如果通过反射创建,就分几种情况需要考虑了(静态构造函数不再此考虑之内):

  1. 有公开的无参构造函数
  2. 只有一个公开的构造函数,并且有非无参构造函数
  3. 有多个构造函数

下面是Service内部的实现代码,可能是我下的版本问题,这个方法存在巨大问题,首先是这里面标识着一个大大的[todo]

[“ // TODO: actual service-fulfillment constructor selection”,也就是说第三种情况,开源的代码并没有实现,所以我们使用的时候,注入的类中不要有2个能够公开访问的非静态构造函数(虽然日常我们开发的一般都不会有,或者会按照规范进行开发,但是第三方的类是保不住的)。]

 public IServiceCallSite CreateCallSite(ServiceProvider provider, ISet<Type> callSiteChain){ConstructorInfo[] constructors = _descriptor.ImplementationType.GetTypeInfo().DeclaredConstructors.Where(IsInjectable).ToArray();// TODO: actual service-fulfillment constructor selectionif (constructors.Length == 1) { ParameterInfo[] parameters = constructors[0].GetParameters(); IServiceCallSite[] parameterCallSites = new IServiceCallSite[parameters.Length]; for (var index = 0; index != parameters.Length; ++index) { parameterCallSites[index] = provider.GetServiceCallSite(parameters[index].ParameterType, callSiteChain); if (parameterCallSites[index] == null && parameters[index].HasDefaultValue) { parameterCallSites[index] = new ConstantCallSite(parameters[index].DefaultValue); } if (parameterCallSites[index] == null) { throw new InvalidOperationException(Resources.FormatCannotResolveService( parameters[index].ParameterType, _descriptor.ImplementationType)); } } return new ConstructorCallSite(constructors[0], parameterCallSites); } return new CreateInstanceCallSite(_descriptor); }

CreateCallSite

即使是没有实现第三种情况下,这个方法也是相当复杂的,下面我们进一步解析。

ConstructorInfo[] constructors = _descriptor.ImplementationType.GetTypeInfo()
                .DeclaredConstructors
                .Where(IsInjectable)
                .ToArray();

这部分代码是获取注册的注入类型的所有公开的构造函数,其中IsInjectable,是获取包含非无参,非静态构造函数的func,代码如下所示:

 private static bool IsInjectable(ConstructorInfo constructor){return constructor.IsPublic && constructor.GetParameters().Length != 0;}

IsInjectable

最结尾处:return new CreateInstanceCallSite(_descriptor);是使用无参构造函数进行反射

private class CreateInstanceCallSite : IServiceCallSite{private readonly ServiceDescriptor _descriptor;public CreateInstanceCallSite(ServiceDescriptor descriptor){_descriptor = descriptor;}public object Invoke(ServiceProvider provider){try{return Activator.CreateInstance(_descriptor.ImplementationType);}catch (Exception ex) when (ex.InnerException != null){ExceptionDispatchInfo.Capture(ex.InnerException).Throw();// The above line will always throw, but the compiler requires we throw explicitly.throw;}}public Expression Build(Expression provider){return Expression.New(_descriptor.ImplementationType);}}

CreateInstanceCallSite

现在所有的问题就落入了如何根据类型,并且该类型只有一个有参的构造函数。

如果我们想根据反射调用类型的有参的构造函数,那么就需要知道参数对象的实例;参数对象的实例可能已经存在,也可能是依赖注入的。如果是依赖注入的,那么是实例注入或者工厂注入,那么问题不大。但是如果都不是而是类型注入,有需要考虑该类型注入是否有一有参的构造函数。如此该函数内部必然有递归调用。所以我们需要遍历所有的parameters(ParameterInfo[] parameters = constructors[0].GetParameters()),首先根据ServiceProvider*对象的GetService*方法去获取参数实例,如果获取的结果为null则,判断该参数是否有默认值,没有默认值则抛出异常,当全部参数参数都齐备后,使用创建ConstructorCallSite实例返回。

[*ServiceProvider对象是创建所有注入类的接口,所以实例参数自然也使用这该类创建]

[*实际上调用的不是GetService方法,而是GetServiceCallSite方法。获取的不是参数的实例,而是能够创建参数实例的IServiceCallSite的对象。所以最后创建的ConstructorCallSite参数也略有不同;并且对于返回为null但有默认值的参数,也需要使用ConstantCallSite进行包装]

如下所示:ConstantCallSite和ConstructorCallSite的源码

private class ConstantCallSite : IServiceCallSite{private readonly object _defaultValue;public ConstantCallSite(object defaultValue){_defaultValue = defaultValue;}public object Invoke(ServiceProvider provider){return _defaultValue;}public Expression Build(Expression provider){return Expression.Constant(_defaultValue);}}

ConstantCallSite

 private class ConstructorCallSite : IServiceCallSite{private readonly ConstructorInfo _constructorInfo;private readonly IServiceCallSite[] _parameterCallSites;public ConstructorCallSite(ConstructorInfo constructorInfo, IServiceCallSite[] parameterCallSites){_constructorInfo = constructorInfo;_parameterCallSites = parameterCallSites;}public object Invoke(ServiceProvider provider){object[] parameterValues = new object[_parameterCallSites.Length];for (var index = 0; index != parameterValues.Length; ++index){parameterValues[index] = _parameterCallSites[index].Invoke(provider);}try{return _constructorInfo.Invoke(parameterValues);}catch (Exception ex) when (ex.InnerException != null){ExceptionDispatchInfo.Capture(ex.InnerException).Throw();// The above line will always throw, but the compiler requires we throw explicitly.throw;}}public Expression Build(Expression provider){var parameters = _constructorInfo.GetParameters();return Expression.New(_constructorInfo,_parameterCallSites.Select((callSite, index) =>Expression.Convert(callSite.Build(provider),parameters[index].ParameterType)));}}

ConstructorCallSite

最后由于核心注入类ServiceProvider无法为未进行注册的简单类型进行转化,所以默认的构造函数中包含基本类型直接注册会出错的,代码如下所示:

public interface IThrowError{}
public class ThrowError{public ThrowError(bool throw){}
}public static IServiceCollection DefaultServices(){var services = new ServiceCollection();services.AddTransient<IThrowError, ThrowError>();return services;
}

但是为基本类型提供默认值,即将ThrowError的构造函数修改为public ThrowError(bool throw=false){},是不会出错的。
当然也可以使用工厂为即有默认值,但是又没有默认值构造函数的类。如下面所示:

services.AddTransient<IFactoryService>(provider =>{var fakeService = provider.GetService<IFakeService>();return new TransientFactoryService{FakeService = fakeService,Value = 42};});

工厂注册

转载于:https://www.cnblogs.com/watermoon2/p/DependencyInjection2.html

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

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

    Asp.net 5的依赖注入注入系列可以参考链接: [Asp.net 5] DependencyInjection项目代码分析-目录 我们在之前讲微软的实现时,对于OpenIEnumerableSer ...

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

    微软DI文章系列如下所示: [Asp.net 5] DependencyInjection项目代码分析 [Asp.net 5] DependencyInjection项目代码分析2-Autofac [ ...

  3. 20189200余超 2018-2019-2 移动平台应用开发实践作项目代码分析

    20189200余超 2018-2019-2 移动平台应用开发实践作项目代码分析 项目名称 小说阅读器 项目功能 注册登录 用户信息.用户密码.用户图像修改 书籍分类 书架 书籍搜索(作者名或书籍名) ...

  4. 团队项目代码分析(Android游戏:别踩白块儿)

    代码组成部分: 关键代码主要分为三大部分,如下图所示(用思维导图的形式展示): 代码调用关系 通过MainActivity调用其他类❤,具体见核心代码分析! 核心代码分析 public class P ...

  5. java rcp 开发 eclipse_Eclipse RCP开发(三):RCP项目代码分析

    plugin.xml Eclipse默认用plugin manifest editor打开plugin.xml,主要有如下几个标签页: 1)Overview 显示项目基本信息,其中Test区域的按钮可 ...

  6. SSM简单项目代码分析

    从朋友那里看了一个简单的ssm三层框架的学生管理系统的代码,开始分析理解其中的代码 代码git 1.整体架构和思路 由Controller,Service,DAO三层逐层调用实现学生管理 主界面是in ...

  7. asp.net mvc 微信支付代码分析(根据沐雪微信平台3.1商城业务来分析)

    开发微信应用,微信支付是永远要面对的.现在的微信支付相对以往已经很稳定,很少出现诡异情况.再加上无数人开发的经验分享,现在开发微信支付已经没什么难度了. 我这次主要是想基于沐雪微信平台的微商城业务来分 ...

  8. 2021SC@SDUSC HBase(六)项目代码分析——Region机制(三)之Region定位

    2021SC@SDUSC 目录 一.初步认识region定位 Meta表 Region定位 二.Region定位 总结 一.初步认识region定位 在 HBase 中,表的所有行都是按照 RowKe ...

  9. 2021SC@SDUSC HBase(十三)项目代码分析——WAL写入

    2021SC@SDUSC 目录 一.简述 二.机制 三.线程模型 四.具体实现 五.总结 一.简述 Hbase 的 WAL 机制是保证 hbase 使用 lsm 树存储模型把随机写转化成顺序写,并从内 ...

最新文章

  1. Hbase 常见错误总结——摘自忘了
  2. pythonurllib模块-Python3学习笔记(urllib模块的使用)
  3. 用ConfigParser模块读写conf配置文件
  4. HTML5主要新增标签
  5. java 兔子生仔问题
  6. python 保存pdf文件_PyPDF2读取PDF文件内容保存到本地TXT实例
  7. 2015/8/26 Python基础(1):基本规则及赋值
  8. 双屏不同缩放比例_科技产品大赏,曲面、折叠手机之后,双屏电脑会是下一个趋势吗?...
  9. 头的各个部位示意图_图解圆锥破碎机润滑油路,7个主要润滑部位特点分析
  10. Spring: 代理模式JDK, CGlib, ASM
  11. 6种 分布式限流方案,我替你整理好了
  12. 芯片烧录器编程AT24C02
  13. uva11401:Triangle Counting 递推 数学
  14. 虚拟机 Linux root登录
  15. 农产品追溯管理平台解决方案
  16. 笔记本电脑实现内外网双通成功经验分享(内网用有线,外网用无线)
  17. mysql之联合索引
  18. JAVASE点餐系统源码
  19. Maben 编译 jenjins配置
  20. 30V转5V,30V转3.3V,30V转3V的5款降压芯片和LDO

热门文章

  1. Axis2创建WebService实例
  2. Oracle 与 MySQL 的区别
  3. awk学习笔记八-Actions
  4. ASP.NET ActionPack
  5. L1-061 新胖子公式 (10 分)-PAT 团体程序设计天梯赛 GPLT
  6. [Python] L1-023. 输出GPLT-PAT团体程序设计天梯赛GPLT
  7. ui li 菜单 点击添加下级_如何制作导航栏中的下拉菜单
  8. 固件是通用的吗_冷镦和冷挤压是一回事吗,两者有什么区别?
  9. google lab 深度学习_吴恩达、李飞飞等化身超级英雄!深度学习漫画第一卷现已开源...
  10. angularjs过滤器(number)