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


    在 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 selection
                if (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
                    };
                });
    工厂注册
  • 相关阅读:
    ES6/ES2015核心内容 import export
    JavaScript 标准参考教程(alpha) 阮一峰
    NPM 学习笔记整理
    (尚026)Vue_案例_动态初始化显示(尚025)
    (尚025)Vue_案例_静态组件
    (尚024)Vue_案例_交互删除
    (尚023)Vue_案例_交互添加
    (尚022)Vue案例_初始化显示(十分详细!!!)
    (尚021)Vue_eslint编码规范检查
    (尚020)Vue打包发布项目
  • 原文地址:https://www.cnblogs.com/watermoon2/p/DependencyInjection2.html
Copyright © 2020-2023  润新知