Asp.Net Core基础篇之:依赖注入DependencyInjection

依赖注入已经不是什么新鲜话题了,在.NET Framework时期就已经出现了各种依赖注入框架,比如:autofacunity等。只是在.net core微软将它搬上了台面,不用再依赖第三方组件(那是不可能的)。依赖注入的概念与为什么选择使用依赖注入这里就不说了,网上搜一下就会有各种答案,今天这里的内容是看看在.net core中,简单实用的依赖注入,背后到底做了哪些操作。

创建项目

今天演示不采用asp.net core项目,而是采用.net core控制台。相对前者,后者的操作逻辑更加完整简洁。

准备接口与对象,老User

复制
public class UserService : IUserService { public string getName() { return "my name is dotnetboy"; } } public interface IUserService { string getName(); }
复制
/// <summary> /// 入口方法 /// </summary> /// <param name="args"></param> public static void Main(string[] args) { // 1、实例化服务容器 IServiceCollection services = new ServiceCollection(); // 2、添加服务 services.AddTransient<IUserService, UserService>(); // 3、构建服务提供对象 IServiceProvider serviceProvider = services.BuildServiceProvider(); // 4、解析并获取服务对象 var service = serviceProvider.GetService<IUserService>(); // 5、调用 Console.WriteLine(service.getName()); }

1、实例化服务容器

复制
IServiceCollection services = new ServiceCollection();

这里出现了一个新对象:IServiceCollectionService,也就是startup中的

复制
public void ConfigureServices(IServiceCollection services){}

F12 可以看到,IServiceCollectionService 继承了IList<ServiceDescriptor>接口,又引申出 ServiceDescriptor 对象。

复制
// // 摘要: // Specifies the contract for a collection of service descriptors. public interface IServiceCollection : IList<ServiceDescriptor>, ICollection<ServiceDescriptor>, IEnumerable<ServiceDescriptor>, IEnumerable { }

ServiceDescriptor 对象见名知意就知道是用来描述服务信息的对象了。ServiceDescriptor 对象的内容有点多,在这里我们暂时只需要了解三个:

复制
/// <summary> /// Describes a service with its service type, implementation, and lifetime. /// </summary> [DebuggerDisplay("Lifetime = {Lifetime}, ServiceType = {ServiceType}, ImplementationType = {ImplementationType}")] public class ServiceDescriptor { /// 生命周期 public ServiceLifetime Lifetime { get; } /// 服务对象 public Type ServiceType { get; } /// 服务实现对象 public Type ImplementationType { get; } }
  • Lifetime:生命周期

  • SericeType:服务对象

  • ImplementationType:服务实现对象

第一步内容比较简单,就是声明一个服务容器集合。我们可以把 IServiceCollection 比做成银行,把服务比喻成 RMB ,现在银行有了,我们下一步肯定就是存 RMB 进去了。

2、添加服务

上面我们提到了 ServiceDescriptor 对象的三个属性:LifetimeServiceTypeImplementationType

再回过头看 services.AddTransient<IUserService, UserService>(); 这段代码

  • AddTransient 指定了 Lifetime,也就是 Transient

  • IUserService 表示 ServiceType

  • UserService 表示 ImplementationType

下面是 AddTransient 相关的源码,很是直观明了。就是将 RMB 存储银行,到底是 活期定期还是理财就由开发者自己去定义了。

复制
public static IServiceCollection AddTransient( this IServiceCollection services, Type serviceType, Type implementationType) { ...... return Add(services, serviceType, implementationType, ServiceLifetime.Transient); }
复制
private static IServiceCollection Add( IServiceCollection collection, Type serviceType, Type implementationType, ServiceLifetime lifetime) { var descriptor = new ServiceDescriptor(serviceType, implementationType, lifetime); collection.Add(descriptor); return collection; }

3、构建服务提供对象

上面两步,有了银行,并且将RMB存进去了。接下来我要买包子没钱,这时候就需要将RMB再取出来。但是存的时候我是在不同的网点存的,取的时候我想在手机银行APP上取,这第三步就是为了构建手机银行APP这个角色。

复制
IServiceProvider serviceProvider = services.BuildServiceProvider();

在这一步会引入一个新对象 ServiceProvider ,也就是给我们提供服务的对象,和 ServiceProviderEngine ,服务提供引擎,姑且这么叫吧。

复制
internal class DynamicServiceProviderEngine : CompiledServiceProviderEngine : ServiceProviderEngine

仔细看下面这段源码(去除不相关部分),就是简单实例化一个 ServiceProvider 对象,ServiceProvider 对象包含一个 IServiceProviderEngine 属性,在 ServiceProvider 对象的构造函数内实例化并赋值。

复制
public static ServiceProvider BuildServiceProvider(this IServiceCollection services, ServiceProviderOptions options) { ...... return new ServiceProvider(services, options); }
复制
private readonly IServiceProviderEngine _engine; // serviceDescriptors:服务集合,options:服务提供者类型 internal ServiceProvider(IEnumerable<ServiceDescriptor> serviceDescriptors, ServiceProviderOptions options) { IServiceProviderEngineCallback callback = null; switch (options.Mode) { case ServiceProviderMode.Default: _engine = new DynamicServiceProviderEngine(serviceDescriptors, callback); } }

ServiceProviderEngine 对象的内容比较多,由于上面代码只做了实例化,所以我们也只看与实例化相关的构造函数代码。

复制
internal abstract class ServiceProviderEngine : IServiceProviderEngine, IServiceScopeFactory { private readonly Func<Type, Func<ServiceProviderEngineScope, object>> _createServiceAccessor; internal ConcurrentDictionary<Type, Func<ServiceProviderEngineScope, object>> RealizedServices { get; } public ServiceProviderEngineScope Root { get; } public IServiceScope RootScope => Root; protected CallSiteRuntimeResolver RuntimeResolver { get; } internal CallSiteFactory CallSiteFactory { get; } protected ServiceProviderEngine(IEnumerable<ServiceDescriptor> serviceDescriptors, IServiceProviderEngineCallback callback) { _createServiceAccessor = CreateServiceAccessor; Root = new ServiceProviderEngineScope(this); RuntimeResolver = new CallSiteRuntimeResolver(); CallSiteFactory = new CallSiteFactory(serviceDescriptors); CallSiteFactory.Add(typeof(IServiceProvider), new ServiceProviderCallSite()); CallSiteFactory.Add(typeof(IServiceScopeFactory), new ServiceScopeFactoryCallSite()); RealizedServices = new ConcurrentDictionary<Type,Func<ServiceProviderEngineScope, object>>(); } }

看了上面的构造函数,哇,又多了这么多新对象,无从下手是不是。这时候我们记住一点,RMB 存到了银行,所以我们就盯着银行:ServiceDescriptor 的动静,发现银行与 CallSiteFactory 这个对象有关联,CallSiteFactory 对象实例化需要银行(serviceDescriptors)。

复制
CallSiteFactory = new CallSiteFactory(serviceDescriptors); CallSiteFactory.Add(typeof(IServiceProvider), new ServiceProviderCallSite()); CallSiteFactory.Add(typeof(IServiceScopeFactory), new ServiceScopeFactoryCallSite());
复制
private readonly List<ServiceDescriptor> _descriptors; private readonly Dictionary<Type, ServiceDescriptorCacheItem> _descriptorLookup = new Dictionary<Type, ServiceDescriptorCacheItem>(); private readonly StackGuard _stackGuard; public CallSiteFactory(IEnumerable<ServiceDescriptor> descriptors) { _stackGuard = new StackGuard(); _descriptors = descriptors.ToList(); Populate(); } private void Populate() { foreach (var descriptor in _descriptors) { var serviceTypeInfo = descriptor.ServiceType.GetTypeInfo(); ...... var cacheKey = descriptor.ServiceType; _descriptorLookup.TryGetValue(cacheKey, out var cacheItem); _descriptorLookup[cacheKey] = cacheItem.Add(descriptor); } }

进入到 CallSiteFactory 对象内逻辑比较清晰,就是将我们银行内的 RMB(服务) 信息遍历并存储到相关字典集合内(_descriptorLookup),给后续逻辑提供查找验证服务。

4、获取对象

上一步手机银行App的角色已经构建好了,这一步要开始取RMB了,取RMB需要什么?密码呗,这里的密码就是 IUserService

复制
var service = serviceProvider.GetService<IUserService>();

我们接下来看看取钱这一步微软都做了些什么操作,就是下面这段代码了:

复制
internal object GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope) { ...... // 已经实现的服务 var realizedService = RealizedServices.GetOrAdd(serviceType, _createServiceAccessor); // _callback?.OnResolve(serviceType, serviceProviderEngineScope); ...... return realizedService.Invoke(serviceProviderEngineScope); }

一眼看去,有效代码其实就一行,涉及到三个对象:RealizedServicesserviceType_createServiceAccessor,都在上一步中出现过。

复制
RealizedServices.GetOrAdd(serviceType, _createServiceAccessor);
复制
private readonly Func<Type, Func<ServiceProviderEngineScope, object>> _createServiceAccessor = CreateServiceAccessor; internal ConcurrentDictionary<Type, Func<ServiceProviderEngineScope, object>> RealizedServices { get; }

我们将上面的代码发散一下,CreateServiceAccessor 就成了我们要研究的重点。

复制
var csa = CreateServiceAccessor(serviceType); RealizedServices.GetOrAdd(serviceType, csa);
复制
private Func<ServiceProviderEngineScope, object> CreateServiceAccessor(Type serviceType) { var callSite = CallSiteFactory.GetCallSite(serviceType, new CallSiteChain()); if (callSite != null) { ...... return RealizeService(callSite); } return _ => null; }

CreateServiceAccessor 方法内的有效代码是两行,我们一步步来深入:

复制
CallSiteFactory.GetCallSite(serviceType, new CallSiteChain());

进入到 GetCallSite 方法内部,一层层剥离

复制
// 第一层 internal ServiceCallSite GetCallSite(Type serviceType, CallSiteChain callSiteChain) { return _callSiteCache.GetOrAdd(serviceType, type => CreateCallSite(type, callSiteChain)); } // 第二层 private ServiceCallSite CreateCallSite(Type serviceType, CallSiteChain callSiteChain) { ...... var callSite = TryCreateExact(serviceType, callSiteChain) ?? TryCreateOpenGeneric(serviceType, callSiteChain) ?? TryCreateEnumerable(serviceType, callSiteChain); _callSiteCache[serviceType] = callSite; return callSite; } // 第三层 private ServiceCallSite TryCreateExact(ServiceDescriptor descriptor, Type serviceType, CallSiteChain callSiteChain, int slot) { if (serviceType == descriptor.ServiceType) { ServiceCallSite callSite; var lifetime = new ResultCache(descriptor.Lifetime, serviceType, slot); ...... new ServiceCallSite(......); return callSite; } return null; }

上面的代码都是围绕 ServiceCallSite 对象的创建再流转,依然是在为最后的取RMB(服务)做准备工作,我们注意一下这段代码:

复制
var lifetime = new ResultCache(descriptor.Lifetime, serviceType, slot);

出现了一个熟悉的对象:Lifetime,也就是我们注册服务的生命周期类型。

复制
public ResultCache(ServiceLifetime lifetime, Type type, int slot) { switch (lifetime) { case ServiceLifetime.Singleton: Location = CallSiteResultCacheLocation.Root; break; case ServiceLifetime.Scoped: Location = CallSiteResultCacheLocation.Scope; break; case ServiceLifetime.Transient: Location = CallSiteResultCacheLocation.Dispose; break; default: Location = CallSiteResultCacheLocation.None; break; } Key = new ServiceCacheKey(type, slot); } public CallSiteResultCacheLocation Location { get; set; } public ServiceCacheKey Key { get; set; }

到现在,准备工作的相关代码都已经走完了,下面就是最后一步:获取/构建对象

复制
return RealizeService(callSite);
复制
protected override Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite) { var realizedService = ResolverBuilder.Build(callSite); RealizedServices[callSite.ServiceType] = realizedService; return realizedService; }
复制
// singleton public Func<ServiceProviderEngineScope, object> Build(ServiceCallSite callSite) { if (callSite.Cache.Location == CallSiteResultCacheLocation.Root) { var value = _runtimeResolver.Resolve(callSite, _rootScope); return scope => value; } return BuildType(callSite).Lambda; } // Scoped private GeneratedMethod BuildType(ServiceCallSite callSite) { if (callSite.Cache.Location == CallSiteResultCacheLocation.Scope) { return _scopeResolverCache.GetOrAdd(callSite.Cache.Key, _buildTypeDelegate, callSite); } return BuildTypeNoCache(callSite); } // Transient private GeneratedMethod BuildTypeNoCache(ServiceCallSite callSite) { var dynamicMethod = new DynamicMethod("ResolveService", attributes: MethodAttributes.Public | MethodAttributes.Static, callingConvention: CallingConventions.Standard, returnType: typeof(object), parameterTypes: new[] { typeof(ILEmitResolverBuilderRuntimeContext), typeof(ServiceProviderEngineScope) }, owner: GetType(), skipVisibility: true); var ilGenerator = dynamicMethod.GetILGenerator(512); ...... }

上面一段代码就是最终构建服务的代码了,前面的所有内容都是在为这一步做准备,具体代码构建的逻辑这篇文章就不讲了,有点技穷。看Transient:BuildTypeNoCache方法内容,可以发现微软是通过 IL 去动态生成的服务。这只是里面的一种方式,还有另外一种方式大家可以自行去研究。


最后,写着写着就发现,有点把握不住。尽管在调式的时候对里面的一些代码的作用,以及怎么运转都有一些理解。但是写出来就不是那么回事,漏洞百出,索性贴出关键源码,记录一下这两天的研究成果。有条件的朋友可以自己去调式一遍源码,比看什么博客有效果多了。

我这里使用的是:JetBrains Rider ,调试源码比较方便,不用手动下载源码。

如果习惯了 vs 的同学可以去 github 上将源码下载下来通过 vs 去调试。

posted @   畅饮无绪  阅读(834)  评论(6编辑  收藏  举报
点击右上角即可分享
微信分享提示