[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); } }
[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); } }
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); } }
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)); } } }
对于ServiceDescriptor对象,只有提供ImplementationInstance和ImplementationFactory都没有提供的时候,也就是只提供ImplementationType才会创建Service类。所以在Service类内部想创建对象的实例,只有通过反射创建对象这一条路可走了。如果通过反射创建,就分几种情况需要考虑了(静态构造函数不再此考虑之内):
- 有公开的无参构造函数
- 只有一个公开的构造函数,并且有非无参构造函数
- 有多个构造函数
下面是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);
}
即使是没有实现第三种情况下,这个方法也是相当复杂的,下面我们进一步解析。
ConstructorInfo[] constructors = _descriptor.ImplementationType.GetTypeInfo()
.DeclaredConstructors
.Where(IsInjectable)
.ToArray();
这部分代码是获取注册的注入类型的所有公开的构造函数,其中IsInjectable,是获取包含非无参,非静态构造函数的func,代码如下所示:
private static bool IsInjectable(ConstructorInfo constructor) { return constructor.IsPublic && constructor.GetParameters().Length != 0; }
最结尾处: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); } }
现在所有的问题就落入了如何根据类型,并且该类型只有一个有参的构造函数。
如果我们想根据反射调用类型的有参的构造函数,那么就需要知道参数对象的实例;参数对象的实例可能已经存在,也可能是依赖注入的。如果是依赖注入的,那么是实例注入或者工厂注入,那么问题不大。但是如果都不是而是类型注入,有需要考虑该类型注入是否有一有参的构造函数。如此该函数内部必然有递归调用。所以我们需要遍历所有的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); } }
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))); } }
最后由于核心注入类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 }; });