相信使用过Asp.Net Core开发框架的人对自带的DI框架已经至关熟悉了,不少刚开始接触.Net Core的时候以为不适应,主要就是由于Core默认集成它的缘由。它是Asp.Net Core基础核心框架之一,对于Asp.Net Core来讲DI就灵魂,已经深刻到这框架的骨髓里了。对于IOC和DI,可能每一个人都能说出本身的理解。IOC全称是Inversion of Control翻译成中文叫控制反转,简单的说就是把对象的控制权反转到IOC容器中,由IOC管理其生命周期。DI全称是DependencyInjection翻译成中文叫依赖注入,就是IOC容器把你依赖的模块经过注入的方式提供给你,而不是你本身主动去获取,其形式主要分为构造注入和属性注入,Core自带的DI只支持构造注入,至于为何,最多的说法就是构造注入能使得依赖变得更清晰,我既然依赖你,那么我实例化的时候你就必须得出现。而构造函数偏偏就承担着这种责任。git
不少人接触它的时候应该都是从Asp.Net Core学习过程当中开始的。其实它自己对Asp.Net Core并没有依赖关系,Asp.Net Core依赖DI,可是这套框架自己并不仅是能够提供给Asp.Net Core使用,它是一套独立的框架,开源在微软官方Github的extensions仓库中具体地址是https://github.com/dotnet/extensions/tree/v3.1.5/src/DependencyInjection。关于如何使用,这里就再也不多说了,相信你们都很是清楚了。那我们就说点不同的。github
咱们都知道提供注册的服务名称叫IServiceCollection,咱们大部分状况下主要使用它的AddScoped、AddTransient、AddSingleton来完成注册。咱们就先查看一下IServiceCollection接口的具体实现,找到源码位置框架
public interface IServiceCollection : IList<ServiceDescriptor> { }
(⊙o⊙)…额,你并无看错,此次我真没少贴代码,其实IServiceCollection本质就是IList
/// <summary> /// 经过泛型注册 /// </summary> public static IServiceCollection AddTransient<TService, TImplementation>(this IServiceCollection services) where TService : class where TImplementation : class, TService { if (services == null) { throw new ArgumentNullException(nameof(services)); } //获得泛型类型 return services.AddTransient(typeof(TService), typeof(TImplementation)); } /// <summary> /// 根据类型注册 /// </summary> public static IServiceCollection AddTransient( this IServiceCollection services, Type serviceType, Type implementationType) { if (services == null) { throw new ArgumentNullException(nameof(services)); } if (serviceType == null) { throw new ArgumentNullException(nameof(serviceType)); } if (implementationType == null) { throw new ArgumentNullException(nameof(implementationType)); } return Add(services, serviceType, implementationType, ServiceLifetime.Transient); } /// <summary> /// 根据类型实例来自工厂注册方法 /// </summary> public static IServiceCollection AddTransient( this IServiceCollection services, Type serviceType, Func<IServiceProvider, object> implementationFactory) { if (services == null) { throw new ArgumentNullException(nameof(services)); } if (serviceType == null) { throw new ArgumentNullException(nameof(serviceType)); } if (implementationFactory == null) { throw new ArgumentNullException(nameof(implementationFactory)); } return Add(services, serviceType, implementationFactory, ServiceLifetime.Transient); }
经过以上代码咱们能够获得两个结论,一是注册服务的方法本质都是在调用Add重载的两个方法,二是声明周期最终仍是经过ServiceLifetime来控制的AddScoped、AddTransient、AddSingleton只是分文别类的进行封装而已,咱们来看ServiceLifetime的源码实现函数
public enum ServiceLifetime { /// <summary> /// 指定将建立服务的单个实例。 /// </summary> Singleton, /// <summary> /// 指定每一个做用域建立服务的新实例。 /// </summary> Scoped, /// <summary> /// 指定每次请求服务时都将建立该服务的新实例。 /// </summary> Transient }
这个枚举是为了枚举咱们注册服务实例的声明周期的,很是清晰不在过多讲述,接下来咱们看核心的两个Add方法的实现学习
private static IServiceCollection Add( IServiceCollection collection, Type serviceType, Type implementationType, ServiceLifetime lifetime) { var descriptor = new ServiceDescriptor(serviceType, implementationType, lifetime); collection.Add(descriptor); return collection; } private static IServiceCollection Add( IServiceCollection collection, Type serviceType, Func<IServiceProvider, object> implementationFactory, ServiceLifetime lifetime) { var descriptor = new ServiceDescriptor(serviceType, implementationFactory, lifetime); collection.Add(descriptor); return collection; }
经过这两个核心方法咱们能够很是清晰的了解到注册的本质其实就是构建ServiceDescriptor实例而后添加到IServiceCollection即IList
public static IServiceCollection AddTransient( this IServiceCollection services, Type serviceType) { if (services == null) { throw new ArgumentNullException(nameof(services)); } if (serviceType == null) { throw new ArgumentNullException(nameof(serviceType)); } //把本身注册给本身 return services.AddTransient(serviceType, serviceType); }
经过这个方法咱们就能够看到其实注册单类型的方法,也是经过调用的注入实例到抽象的方法,只不过是将本身注册给了本身。
好了,抽象和扩展方法咱们就先说到这里,接下来咱们来看IServiceCollection的实现类ServiceCollection的实现this
public class ServiceCollection : IServiceCollection { private readonly List<ServiceDescriptor> _descriptors = new List<ServiceDescriptor>(); public int Count => _descriptors.Count; public bool IsReadOnly => false; public ServiceDescriptor this[int index] { get { return _descriptors[index]; } set { _descriptors[index] = value; } } public void Clear() { _descriptors.Clear(); } public bool Contains(ServiceDescriptor item) { return _descriptors.Contains(item); } public void CopyTo(ServiceDescriptor[] array, int arrayIndex) { _descriptors.CopyTo(array, arrayIndex); } public bool Remove(ServiceDescriptor item) { return _descriptors.Remove(item); } public IEnumerator<ServiceDescriptor> GetEnumerator() { return _descriptors.GetEnumerator(); } void ICollection<ServiceDescriptor>.Add(ServiceDescriptor item) { _descriptors.Add(item); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public int IndexOf(ServiceDescriptor item) { return _descriptors.IndexOf(item); } public void Insert(int index, ServiceDescriptor item) { _descriptors.Insert(index, item); } public void RemoveAt(int index) { _descriptors.RemoveAt(index); } }
这个类就很是清晰,也很是简单了。ServiceCollection承载了一个List
public class ServiceDescriptor { public ServiceDescriptor( Type serviceType, Type implementationType, ServiceLifetime lifetime) : this(serviceType, lifetime) { ImplementationType = implementationType; } public ServiceDescriptor( Type serviceType, object instance) : this(serviceType, ServiceLifetime.Singleton) { ImplementationInstance = instance; } public ServiceDescriptor( Type serviceType, Func<IServiceProvider, object> factory, ServiceLifetime lifetime) : this(serviceType, lifetime) { ImplementationFactory = factory; } private ServiceDescriptor(Type serviceType, ServiceLifetime lifetime) { Lifetime = lifetime; ServiceType = serviceType; } public ServiceLifetime Lifetime { get; } public Type ServiceType { get; } public Type ImplementationType { get; } public object ImplementationInstance { get; } public Func<IServiceProvider, object> ImplementationFactory { get; } }
这里咱们只是粘贴了初始化的方法,经过这个初始化咱们获得了,本质其实就是给描述具体注册的Lifetime、ServiceType、ImplementationType、ImplementationInstance、ImplementationFactory赋值。在平时的使用中,咱们在注册服务的时候还会用到这种注册方式翻译
services.Add(ServiceDescriptor.Scoped<IPersonService, PersonService>()); //services.Add(ServiceDescriptor.Scoped(typeof(IPersonService),typeof(PersonService))); //或 services.Add(ServiceDescriptor.Transient<IPersonService, PersonService>()); //services.Add(ServiceDescriptor.Transient(typeof(IPersonService), typeof(PersonService))); //或 services.Add(ServiceDescriptor.Singleton<IPersonService, PersonService>()); //services.Add(ServiceDescriptor.Singleton(typeof(IPersonService), typeof(PersonService)));
这种注册方式是经过ServiceDescriptor自身的操做去注册相关实例,咱们拿出来其中一个Transient看一下具体实现
public static ServiceDescriptor Transient<TService, TImplementation>() where TService : class where TImplementation : class, TService { //都是在调用Describe return Describe<TService, TImplementation>(ServiceLifetime.Transient); } public static ServiceDescriptor Transient(Type service, Type implementationType) { //都是在调用Describe return Describe(service, implementationType, ServiceLifetime.Transient); } public static ServiceDescriptor Describe(Type serviceType, Type implementationType, ServiceLifetime lifetime) { //仍是返回ServiceDescriptor实例 return new ServiceDescriptor(serviceType, implementationType, lifetime); } public static ServiceDescriptor Describe(Type serviceType, Func<IServiceProvider, object> implementationFactory, ServiceLifetime lifetime) { //仍是返回ServiceDescriptor实例 return new ServiceDescriptor(serviceType, implementationFactory, lifetime); }
经过这个咱们就能够了解到ServiceDescriptor.Scoped、ServiceDescriptor.Singleton、ServiceDescriptor.Singleton实际上是调用的Describe方法,Describe的自己仍是去实例化ServiceDescriptor,异曲同工,只是多了种写法,最终仍是去构建ServiceDescriptor。经过这么多源码的分析得出的结论就一点IServiceCollection注册的本质就是在构建ServiceDescriptor集合。
上面咱们了解到了服务注册相关,至于服务是怎么提供出来的,你们应该都是很是熟悉了实际上是根据IServiceCollection构建出来的
IServiceProvider serviceProvider = services.BuildServiceProvider();
BuildServiceProvider并非IServiceCollection的自带方法,因此也是来自扩展方法,找到ServiceCollectionContainerBuilderExtensions扩展类,最终都是在执行这个方法
public static ServiceProvider BuildServiceProvider(this IServiceCollection services, ServiceProviderOptions options) { return new ServiceProvider(services, options); }
BuildServiceProvider的时候须要传递ServiceProviderOptions这个类主要是配置是否校验做用域和提供的实例来自于那种提供引擎使用
public class ServiceProviderOptions { internal static readonly ServiceProviderOptions Default = new ServiceProviderOptions(); /// <summary> /// 是够在编译的时候校验做用域范围检查 /// </summary> public bool ValidateScopes { get; set; } /// <summary> /// 是够在编译的时候校验做用域范围检查 /// </summary> public bool ValidateOnBuild { get; set; } /// <summary> /// 配置使用那种方式提供ServiceProvider的承载的具体实例 /// </summary> internal ServiceProviderMode Mode { get; set; } = ServiceProviderMode.Default; } internal enum ServiceProviderMode { Default, Dynamic, Runtime, Expressions, ILEmit }
做用域范围检查仍是很是严格的,不开启的也会有必定的依赖规则,简单总结一下
public interface IServiceProvider { object GetService (Type serviceType); }
ServiceProvider是IServiceProvider的默认实现类,它是获取注册实例的默认出口类,咱们只看提供服务相关的
public sealed class ServiceProvider : IServiceProvider, IDisposable, IServiceProviderEngineCallback, IAsyncDisposable { private readonly IServiceProviderEngine _engine; private readonly CallSiteValidator _callSiteValidator; internal ServiceProvider(IEnumerable<ServiceDescriptor> serviceDescriptors, ServiceProviderOptions options) { IServiceProviderEngineCallback callback = null; if (options.ValidateScopes) { callback = this; _callSiteValidator = new CallSiteValidator(); } //根据ServiceProviderMode的值判断才有那种方式去实例化对象 switch (options.Mode) { //默认方式 case ServiceProviderMode.Default: if (RuntimeFeature.IsSupported("IsDynamicCodeCompiled")) { _engine = new DynamicServiceProviderEngine(serviceDescriptors, callback); } else { _engine = new RuntimeServiceProviderEngine(serviceDescriptors, callback); } break; case ServiceProviderMode.Dynamic: _engine = new DynamicServiceProviderEngine(serviceDescriptors, callback); break; case ServiceProviderMode.Runtime: _engine = new RuntimeServiceProviderEngine(serviceDescriptors, callback); break; //if IL_EMIT case ServiceProviderMode.ILEmit: _engine = new ILEmitServiceProviderEngine(serviceDescriptors, callback); break; case ServiceProviderMode.Expressions: _engine = new ExpressionsServiceProviderEngine(serviceDescriptors, callback); break; default: throw new NotSupportedException(nameof(options.Mode)); } //判断是否开启编译时范围校验 if (options.ValidateOnBuild) { List<Exception> exceptions = null; foreach (var serviceDescriptor in serviceDescriptors) { try { _engine.ValidateService(serviceDescriptor); } catch (Exception e) { } } } } /// <summary> /// 经过IServiceProviderEngine获取具体实例的方法 /// </summary> public object GetService(Type serviceType) => _engine.GetService(serviceType); }
在这个类里,关于提供具体实例的操做仍是很是清晰的,关于更深的IServiceProviderEngine这里就不过多介绍了,有兴趣的能够自行在GitHub上查阅。
在声明周期里Scope是比较特殊也是比较抽象的一个,咱们使用的时候是经过当前serviceProvider建立子做用域
using (IServiceScope scope = serviceProvider.CreateScope()) { IServiceProvider scopeProvider = scope.ServiceProvider; }
它大概的思路就是在当前容器中建立一个做用域,scope.ServiceProvider来获取这个子容器做用域里的实例。Singleton类型的实例直接去根容器获取,因此和当前子容器做用域无关。Scoped类型的实例,在当前做用域内惟一,不管获取多少次返回的都是同一个实例。Transient类型的只要去获取都是返回新的实例。当前IServiceScope释放的时候Scoped类型的实例也会被释放,注意!!!Transient类型的实例也是在当前IServiceScope Dispose的时候去释放,尽管你每次获取的时候都是新的实例,可是释放的时候都是统一释放的。在当前ServiceScope内你能够继续建立当前Scope的IServiceScope。其实经过这里也不难发现根容器的Scoped其实就是等同于Singleton,其生命周期都是和应用程序保持一致。
Scope问题在若是写控制台之类的程序其做用可能不是很明显,除非有特殊的要求,在Asp.Net Core中使用仍是比较深刻的。Asp.Net Core在启动的时候会建立serviceProvider,这个serviceProvider的Scope是跟随程序的生命周期一致的,它是做为全部服务实例的根容器。在Asp.Net Core中有几种状况的实例和请求无关也就是说在程序运行期间是单例状况的,咱们使用的时候须要注意的地方
UseServiceProviderFactory方法主要是为咱们提供了替换默认容器的操做,经过这个方法能够将三方的IOC框架结合进来好比Autofac。咱们能够查看UseServiceProviderFactory具体的实现,了解它的工做方式。这个方法来自HostBuilder类
public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder> factory) { _serviceProviderFactory = new ServiceFactoryAdapter<TContainerBuilder>(factory ?? throw new ArgumentNullException(nameof(factory))); return this; }
咱们找到_serviceProviderFactory定义的地方,默认值就是为ServiceFactoryAdapter传递了DefaultServiceProviderFactory实例。
private IServiceFactoryAdapter _serviceProviderFactory = new ServiceFactoryAdapter<IServiceCollection>(new DefaultServiceProviderFactory());
继续查找ServiceFactoryAdapter的大体核心实现
internal class ServiceFactoryAdapter<TContainerBuilder> : IServiceFactoryAdapter { private IServiceProviderFactory<TContainerBuilder> _serviceProviderFactory; public object CreateBuilder(IServiceCollection services) { return _serviceProviderFactory.CreateBuilder(services); } public IServiceProvider CreateServiceProvider(object containerBuilder) { return _serviceProviderFactory.CreateServiceProvider((TContainerBuilder)containerBuilder); } }
经过查找HostBuilder中这段源码咱们能够知道ServiceFactoryAdapter建立出来的容器是供整个Host使用的。也就是说咱们在程序中使用的容器相关的都是由它提供的。
接下来咱们看下默认的DefaultServiceProviderFactory的大体实现。找到源码位置
public class DefaultServiceProviderFactory : IServiceProviderFactory<IServiceCollection> { public IServiceCollection CreateBuilder(IServiceCollection services) { return services; } public IServiceProvider CreateServiceProvider(IServiceCollection containerBuilder) { return containerBuilder.BuildServiceProvider(_options); } }
没啥逻辑,其实就是把默认的IServiceCollection和IServiceProvider经过工厂的形式提供出来。这么作的目的只有一个,就是下降依赖的耦合度方便咱们可以介入第三方的IOC框架。口说无凭,接下来咱们就看一下Autofac是怎么适配进来的。咱们在GitHub上找到Autofac.Extensions.DependencyInjection仓库的位置https://github.com/autofac/Autofac.Extensions.DependencyInjection,找到Autofac中IServiceProviderFactory实现类AutofacServiceProviderFactory,看看他是如何适配到默认的IOC框架的
public class AutofacServiceProviderFactory : IServiceProviderFactory<ContainerBuilder> { private readonly Action<ContainerBuilder> _configurationAction; public AutofacServiceProviderFactory(Action<ContainerBuilder> configurationAction = null) { _configurationAction = configurationAction ?? (builder => { }); } public ContainerBuilder CreateBuilder(IServiceCollection services) { //因为是使用Autofac自己的容器去工做,因此返回的Autofac承载类ContainerBuilder var builder = new ContainerBuilder(); //将现有的IServiceCollection中注册的实例托管到ContainerBuilder中 builder.Populate(services); //这一步是咱们自定义注入到Autofac方法的委托,及咱们在Startup类中定义的 //public void ConfigureContainer(ContainerBuilder builder)方法 _configurationAction(builder); return builder; } public IServiceProvider CreateServiceProvider(ContainerBuilder containerBuilder) { if (containerBuilder == null) throw new ArgumentNullException(nameof(containerBuilder)); //获取Container容器,由于接下来要使用获取实例的方法了 var container = containerBuilder.Build(); //这个类实现了IServiceProvider接口 //实现了public object GetService(Type serviceType)方法从Autofac的Container中获取实例 return new AutofacServiceProvider(container); } }
IServiceProviderFactory的工做其实就是适配符合咱们使用的适配器模式,其核心就是用你的容器去托管注册到IServiceCollection中的服务。而后用你的容器去构建IServiceProvider实例。
经过以上咱们对自带的DependencyInjection工做方式有了必定的了解,并且其扩展性很是强,可以使咱们经过本身的方式去构建服务注册和注入,咱们以Autofac为例讲解了三方容器集成到自带IOC的方式。有不少核心的源码并无讲解到,由于怕本身理解不够,就不误导你们了。我在上文中涉及到源码的地方基本上都加了源码的链接,能够直接点进去查看源码,以前源码探究相关的文章也都是同样,可能以前有许多同窗没有注意到。主要缘由是我粘贴出来的代码有删减,最重要的仍是怕本身理解不到位,误导了你们,这样就能用过点击本身查看源码了。若有你有更好的理解,或者以为我讲解的理解不到的地方,欢迎评论区沟通交流。