hello,大家好,今天仍然是橙子老哥的分享时间,希望大家一起学习,一起进步。
欢迎加入.net意社区,第一时间了解我们的动态,文章第一时间分享至社区
社区官方地址:https://ccnetcore.com
官方微信公众号:搜索 意.Net
添加橙子老哥微信加入官方微信群:chengzilaoge520
> 此篇我们继续接《.NetCore IOC依赖注入源码剖析(一)》,也是依赖注入的终章,解开最后的谜团,难度较高,需要一定时间钻研
备注:微信的排版你懂的,如需pc端完整的体验,可前往意社区查看
1、往期回顾
上期,我们介绍了依赖注入的使用,以及ServiceCollection 服务容器、ServiceProvider 这两大核心对象,细心的小伙伴们已经发现了
无论是在build的时候,还是在获取服务的时候,ServiceProvider 最终都会去调用`CallSiteFactory`的`GetCallSite`,通过`GetCallSite`交给对应的`ServiceProviderEngine `引擎中
以下是整个依赖注入调用链:
ServiceCollection -> (服务容器,将服务新增到容器中)
ServiceProvider -> (服务提供者,服务的提供使用)
(callsite工厂,用于创建对应的CallSite)
(用于引擎所需要解析的服务配置信息)
ServiceProviderEngine ->(通过CallSite具体去实例化对象的引擎)
(服务访问器,用于包装CallSite和实例委托缓存)
object? result-> (通过服务访问器去执行委托实例需要的对象)
ServiceProvider 更像是又包了一层,对`CallSite`和`Engine `的一个封装,所以我们看不到具体它是如何创建对象,一切的未解之谜都丢到了这两个核心对象中
今天,我们将最后的谜团解开
2、CallSiteFactory - CallSite工厂构造函数
有了之前的分析,我们知道,主要是为了使用它的`GetCallSite`方法,去获取对应服务的配置信息,我们先看看它的构造函数
//构造需要服务容器
public CallSiteFactory(ICollection<ServiceDescriptor> descriptors)
{
//堆栈守卫,c#一种数据结构,用于缓存
_stackGuard = new StackGuard();
//服务描述者,new一个新的服务描述者
_descriptors = new ServiceDescriptor[descriptors.Count];
//将传入的服务容器copy一份给自己
descriptors.CopyTo(_descriptors, 0);
//继续初始化
Populate();
}
//这里的代码非常多,主要是为了校验泛型、非泛型的依赖注入类,是否存在问题,我们先去掉
private void Populate()
{
//遍历所有的服务描述者
foreach (ServiceDescriptor descriptor in _descriptors)
{
Type serviceType = descriptor.ServiceType;
//服务描述着,包一层,变成服务身份证
var cacheKey = ServiceIdentifier.FromDescriptor(descriptor);
//下面就是将服务身份证和ServiceDescriptor进行一个缓存操作
//服务描述者查找器
_descriptorLookup.TryGetValue(cacheKey, out ServiceDescriptorCacheItem cacheItem);
_descriptorLookup[cacheKey] = cacheItem.Add(descriptor);
}
}
以上就是构造函数做的事情,我们看到一个新的对象`ServiceIdentifier`服务身份证,这个就是用于找到对应服务描述者的,ServiceType 和ServiceKey 包一层,支持key服务依赖注入,为了保持唯一性
internal readonly struct ServiceIdentifier : IEquatable<ServiceIdentifier>
{
public object? ServiceKey { get; }
public Type ServiceType { get; }
public static ServiceIdentifier FromDescriptor(ServiceDescriptor serviceDescriptor)
{
return new ServiceIdentifier(serviceDescriptor.ServiceKey, serviceDescriptor.ServiceType);
}
我们再回到上面,构造函数中,并没有做什么,我们看看`GetCallSite`方法
3、CallSiteFactory - 获取CallSite方法(递归)
> 事先提个醒,从第一个方法开始,就准备开始递归套娃,请记住遇到的每个方法调用链
private readonly ConcurrentDictionary<ServiceCacheKey, ServiceCallSite> _callSiteCache = new ConcurrentDictionary<ServiceCacheKey, ServiceCallSite>();
internal ServiceCallSite? GetCallSite(ServiceIdentifier serviceIdentifier, CallSiteChain callSiteChain) =>
_callSiteCache.TryGetValue(new ServiceCacheKey(serviceIdentifier, DefaultSlot), out ServiceCallSite? site) ? site :
CreateCallSite(serviceIdentifier, callSiteChain);
这个方法,又先包了一层缓存,如果不存在然后再执行CreateCallSite
private ServiceCallSite? CreateCallSite(ServiceIdentifier serviceIdentifier, CallSiteChain callSiteChain)
{
//堆栈守卫进行缓存判断
if (!_stackGuard.TryEnterOnCurrentStack())
{
return _stackGuard.RunOnEmptyStack(CreateCallSite, serviceIdentifier, callSiteChain);
}
//拿一个锁
var callsiteLock = _callSiteLocks.GetOrAdd(serviceIdentifier, static _ => new object());
lock (callsiteLock)
{
//使用callSiteChain调用链,进行判断是否存在循环依赖注入
callSiteChain.CheckCircularDependency(serviceIdentifier);
//再包一层去TryCreateExact创建CallSite
ServiceCallSite? callSite = TryCreateExact(serviceIdentifier, callSiteChain) ??
//如果没有获取到,去找泛型获取操作
TryCreateOpenGeneric(serviceIdentifier, callSiteChain) ??
//泛型也没找到,再去找IEnumerable类型
TryCreateEnumerable(serviceIdentifier, callSiteChain);
return callSite;
}
}
以上方法,可以看到一个锁,这个是为了防止
// C -> D -> A
// E -> D -> A
当c和e执行的时候,到d和a就重复了,所以有个锁,另外,这里又多了一个对象,callSiteChain-callSite调用链,这个我们先留意一下,这里只是做了一个循环调用的校验,后面肯定又新增进去的对象
internal sealed class CallSiteChain
{
private readonly Dictionary<ServiceIdentifier, CallSiteChain.ChainItemInfo> _callSiteChain;
public CallSiteChain()
{
this._callSiteChain = new Dictionary<ServiceIdentifier, CallSiteChain.ChainItemInfo>();
}
public void CheckCircularDependency(ServiceIdentifier serviceIdentifier)
{
if (this._callSiteChain.ContainsKey(serviceIdentifier))
throw new InvalidOperationException(this.CreateCircularDependencyExceptionMessage(serviceIdentifier));
}
public void Add(ServiceIdentifier serviceIdentifier, Type? implementationType = null)
{
this._callSiteChain[serviceIdentifier] = new CallSiteChain.ChainItemInfo(this._callSiteChain.Count, implementationType);
}
}
这个调用链也很简单,就是一个字典,校验的时候,判断key中是否存在过,ok我们回到`TryCreateExact`方法,包了这么多层,我们还没有走进去
private ServiceCallSite? TryCreateExact(ServiceIdentifier serviceIdentifier, CallSiteChain callSiteChain)
{
//_descriptorLookup 查找器进行缓存
if (_descriptorLookup.TryGetValue(serviceIdentifier, out ServiceDescriptorCacheItem descriptor))
{
return TryCreateExact(descriptor.Last, serviceIdentifier, callSiteChain, DefaultSlot);
}
//这里如果是key服务依赖注入,走这个,key注入
if (serviceIdentifier.ServiceKey != null)
{
// Check if there is a registration with KeyedService.AnyKey
var catchAllIdentifier = new ServiceIdentifier(KeyedService.AnyKey, serviceIdentifier.ServiceType);
if (_descriptorLookup.TryGetValue(catchAllIdentifier, out descriptor))
{
return TryCreateExact(descriptor.Last, serviceIdentifier, callSiteChain, DefaultSlot);
}
}
return null;
}
这里主要是将key依赖注入,和普通的依赖注入进行一个区分,走了一下查找器的缓存,还是包了一层
最终走到TryCreateExact方法,我们再进去看看
private ServiceCallSite? TryCreateExact(ServiceDescriptor descriptor, ServiceIdentifier serviceIdentifier, CallSiteChain callSiteChain, int slot)
{
if (serviceIdentifier.ServiceType == descriptor.ServiceType)
{
//再来一个_callSiteCache缓存
ServiceCacheKey callSiteKey = new ServiceCacheKey(serviceIdentifier, slot);
if (_callSiteCache.TryGetValue(callSiteKey, out ServiceCallSite? serviceCallSite))
{
return serviceCallSite;
}
ServiceCallSite callSite;
//将生命周期和身份证,排序插槽包一层给ResultCache
var lifetime = new ResultCache(descriptor.Lifetime, serviceIdentifier, slot);
//如果实现类是一个实例,使用ConstantCallSite
if (descriptor.HasImplementationInstance())
{
callSite = new ConstantCallSite(descriptor.ServiceType, descriptor.GetImplementationInstance());
}
//如果实现类是一个key服务,并且没有实现工厂使用FactoryCallSite
else if (!descriptor.IsKeyedService && descriptor.ImplementationFactory != null)
{
callSite = new FactoryCallSite(lifetime, descriptor.ServiceType, descriptor.ImplementationFactory);
}
//如果实现类是一个key服务,并且实现工厂使用FactoryCallSite 重载
else if (descriptor.IsKeyedService && descriptor.KeyedImplementationFactory != null)
{
callSite = new FactoryCallSite(lifetime, descriptor.ServiceType, serviceIdentifier.ServiceKey!, descriptor.KeyedImplementationFactory);
}
//如果不是一个实例,而是一个类型,走CreateConstructorCallSite
else if (descriptor.HasImplementationType())
{
callSite = CreateConstructorCallSite(lifetime, serviceIdentifier, descriptor.GetImplementationType()!, callSiteChain);
}
else
{
throw new InvalidOperationException(SR.InvalidServiceDescriptor);
}
//找到了,塞到_callSiteCache缓存
callSite.Key = descriptor.ServiceKey;
return _callSiteCache[callSiteKey] = callSite;
}
return null;
}
TryCreateExact的代码就比较多了,主要是为了区分callsite通过那种方式进行构建
1. ConstantCallSite (存在实例,例如单例模式,丢个实例)
2. FactoryCallSite (key服务依赖注入)
3. ConstructorCallSite (通过类型依赖注入,构造函数)
现在我们走到了ConstructorCallSite ,这里的内容也是比较多的,我们省略一些非核心的,看看它是如何创建的
private ConstructorCallSite CreateConstructorCallSite(
ResultCache lifetime,
ServiceIdentifier serviceIdentifier,
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType,
CallSiteChain callSiteChain)
{
try
{
callSiteChain.Add(serviceIdentifier, implementationType);
//记住这个,在这里将构造函数反射获取到
ConstructorInfo[] constructors = implementationType.GetConstructors();
//ConstructorCallSite 里面又包了很多个ServiceCallSite[]
ServiceCallSite[]? parameterCallSites = null;
//如果没有构造函数,直接报错
if (constructors.Length == 0)
{
throw new InvalidOperationException(SR.Format(SR.NoConstructorMatch, implementationType));
}
//如果构造函数的长度是一个,就是我们想要的
else if (constructors.Length == 1)
{
ConstructorInfo constructor = constructors[0];
//构造函数去获取参数
ParameterInfo[] parameters = constructor.GetParameters();
//如果没有参数了,代表可以返回了,记住这个点,也是一个终结点
if (parameters.Length == 0)
{
//将上面反射获取到的构造函数信息,塞到ConstructorCallSite中,记住这里的构造函数
return new ConstructorCallSite(lifetime, serviceIdentifier.ServiceType, constructor);
}
//如果参数有多个,去创建ArgumentCallSites
parameterCallSites = CreateArgumentCallSites(
serviceIdentifier,
implementationType,
callSiteChain,
parameters,
throwIfCallSiteNotFound: true)!;
//parameterCallSites塞给ConstructorCallSite一个重载
return new ConstructorCallSite(lifetime, serviceIdentifier.ServiceType, constructor, parameterCallSites);
}
Array.Sort(constructors,
(a, b) => b.GetParameters().Length.CompareTo(a.GetParameters().Length));
ConstructorInfo? bestConstructor = null;
HashSet<Type>? bestConstructorParameterTypes = null;
//如果有多个构造函数,遍历每一个去CreateArgumentCallSites
for (int i = 0; i < constructors.Length; i++)
{
ParameterInfo[] parameters = constructors[i].GetParameters();
ServiceCallSite[]? currentParameterCallSites = CreateArgumentCallSites(
serviceIdentifier,
implementationType,
callSiteChain,
parameters,
throwIfCallSiteNotFound: false);
}
}
在这里,我们关注到两个点:
if (parameters.Length == 0)
{
//将上面反射获取到的构造函数信息,塞到ConstructorCallSite中,记住这里的构造函数
return new ConstructorCallSite(lifetime, serviceIdentifier.ServiceType, constructor);
}
//如果参数有多个,去创建ArgumentCallSites
parameterCallSites = CreateArgumentCallSites(
serviceIdentifier,
implementationType,
callSiteChain,
parameters,
throwIfCallSiteNotFound: true)!;
//parameterCallSites塞给ConstructorCallSite一个重载
return new ConstructorCallSite(lifetime, serviceIdentifier.ServiceType, constructor, parameterCallSites);
`if (parameters.Length == 0)` 明显是一个终止节点,而它的参数如何通过CreateArgumentCallSites创建的呢?我们再走进创建参数的地方
private ServiceCallSite[]? CreateArgumentCallSites(
ServiceIdentifier serviceIdentifier,
Type implementationType,
CallSiteChain callSiteChain,
ParameterInfo[] parameters,
bool throwIfCallSiteNotFound)
{
//创建返回结果
var parameterCallSites = new ServiceCallSite[parameters.Length];
//遍历每一个参数
for (int index = 0; index < parameters.Length; index++)
{
ServiceCallSite? callSite = null;
bool isKeyedParameter = false;
Type parameterType = parameters[index].ParameterType;
//这里遍历参数上是否有ServiceKeyAttribute的特性,用于key的依赖注入
foreach (var attribute in parameters[index].GetCustomAttributes(true))
{
if (serviceIdentifier.ServiceKey != null && attribute is ServiceKeyAttribute)
{
// Check if the parameter type matches
if (parameterType != serviceIdentifier.ServiceKey.GetType())
{
throw new InvalidOperationException(SR.InvalidServiceKeyType);
}
callSite = new ConstantCallSite(parameterType, serviceIdentifier.ServiceKey);
break;
}
if (attribute is FromKeyedServicesAttribute keyed)
{
var parameterSvcId = new ServiceIdentifier(keyed.Key, parameterType);
callSite = GetCallSite(parameterSvcId, callSiteChain);
isKeyedParameter = true;
break;
}
}
//如果不是key的参数,调用了GetCallSite
if (!isKeyedParameter)
{
callSite ??= GetCallSite(ServiceIdentifier.FromServiceType(parameterType), callSiteChain);
}
//给结果赋值
parameterCallSites[index] = callSite;
}
return parameterCallSites;
}
这里遍历了每一个参数,同时每个参数走的`CallSiteFactory.GetCallSite`方法,等等!什么?兜兜转转这么一大圈,又回到了`GetCallSite`,我们是在看`GetCallSite`去获取服务配置的方法,结果当它遍历构造函数参数的时候,参数的获取也是通过`GetCallSite`去获取的
> 所以,很明显,这是一个递归调用,根据类型获取服务配置,这个类型有构造函数,构造函数有很多参数,每个参数又根据类型进行创建!
如果是递归,终止条件是什么呢?其实上面也提过了,`if (parameters.Length == 0)` 当构造函数没有参数的时候,直接返回参数的callsite了
至此,已成闭环!整个调用链完成
4、ServiceProviderEngineScope-引擎实例对象
最后一个谜团,通过CallSiteFactory去创建了CallSite,只是将服务的信息配置存储在CallSite中,并未真正的去实例化对象,而做这一步的,就是服务提供者引擎,我们返回到ServiceProvider获取服务的地方(获取服务,肯定要实例化完成才获取的到)
internal object? GetService(ServiceIdentifier serviceIdentifier, ServiceProviderEngineScope serviceProviderEngineScope)
{
if (_disposed)
{
ThrowHelper.ThrowObjectDisposedException();
}
ServiceAccessor serviceAccessor = _serviceAccessors.GetOrAdd(serviceIdentifier, _createServiceAccessor);
OnResolve(serviceAccessor.CallSite, serviceProviderEngineScope);
DependencyInjectionEventSource.Log.ServiceResolved(this, serviceIdentifier.ServiceType);
object? result = serviceAccessor.RealizedService?.Invoke(serviceProviderEngineScope);
System.Diagnostics.Debug.Assert(result is null || CallSiteFactory.IsService(serviceIdentifier));
return result;
}
这里最终创建实例,通过服务访问器,`serviceAccessor.RealizedService?.Invoke`中委托执行创建的,访问访问器我们上节已经讲过,是缓存了获取服务的过程,但是没有执行,这里我们可以看到Invoke塞入了我们的`serviceProviderEngineScope`引擎进去
我们接着上一节,ServiceAccessor中,在ServiceProvider初始化的时候,CreateServiceAccessor就已经将它赋值了,CreateServiceAccessor方法中,调用了引擎通过callSite去实例化
Func<ServiceProviderEngineScope, object?> realizedService = _engine.RealizeService(callSite);
callSite,我们已经获取到了,这里本质是调用引擎的RealizeService方法,我们找到对应的引擎看看
public override Func<ServiceProviderEngineScope, object?> RealizeService(
ServiceCallSite callSite)
{
return (Func<ServiceProviderEngineScope, object>) (scope => CallSiteRuntimeResolver.Instance.Resolve(callSite, scope));
}
这里也是包了跳转很多层,根据不同的callsite类型执行不同的方法,如果是构造函数,最终执行到
protected override object VisitConstructor(
ConstructorCallSite constructorCallSite,
RuntimeResolverContext context)
{
object[] parameters;
//构造函数参数是0,我们不用去创建参数
if (constructorCallSite.ParameterCallSites.Length == 0)
{
parameters = Array.Empty<object>();
}
else
{
//如果ParameterCallSites大于0
parameters = new object[constructorCallSite.ParameterCallSites.Length];
//再包一层
for (int index = 0; index < parameters.Length; ++index)
parameters[index] = this.VisitCallSite(constructorCallSite.ParameterCallSites[index], context);
}
//直接通过ConstructorInfo,和 parameters 进行实例化
return constructorCallSite.ConstructorInfo.Invoke(BindingFlags.DoNotWrapExceptions, (Binder) null, parameters, (CultureInfo) null);
}
到这里,我们还要注意2个关键点,
1. ConstructorInfo是哪里来的?
2. 为什么可以通过ConstructorInfo.Invoke进行实例化?
问题1:我们往callsite进行回顾,callsite中去构造函数解析的时候,不是将构造函数反射一起塞到了callsite中吗?然后现在又传给了引擎,所以是callsite来的
问题2:ConstructorInfo是c#反射的一个类,可以通过构造函数和参数进行实例化,不清楚的小伙伴可以看看
这里也是这个引擎的真正核心的一点,拿callsite的ConstructorInfo和它解析的参数配置,做一个构造函数反射的Invoke,最终实例化出了我们想要的服务对象
5、总结
> 依赖注入的本质,递归构造函数参数+构造函数反射实例化
本篇难度较高,如果你能看到这,恭喜你,看到了这里。
我们回到前一节的开头,现在,你还敢说IOC简单吗?
.Net意社区,高频发布原创有深度的.Net相关知识内容
与你一起学习,一起进步