依賴注入已經不是什么新鮮話題了,在.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
去調試。