依赖注入已经不是什么新鲜话题了,在.NET Framework
时期就已经出现了各种依赖注入框架,比如:autofac
、unity
等。只是在.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
对象的三个属性:Lifetime
、ServiceType
、ImplementationType
。
再回过头看 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);
}
一眼看去,有效代码其实就一行,涉及到三个对象:RealizedServices
、serviceType
、_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
去调试。