[Asp.net 5] DependencyInjection项目代码分析4-微软的实现(4)
分类:
IT文章
•
2022-06-17 20:46:18
这个系列已经写了6篇,链接地址如下:
如果想对本篇有个更好的了解,建议需要先看
“[Asp.net 5] DependencyInjection项目代码分析”
“[Asp.net 5] DependencyInjection项目代码分析4-微软的实现(1)”
“[Asp.net 5] DependencyInjection项目代码分析4-微软的实现(2)”。
"[Asp.net 5] DependencyInjection项目代码分析4-微软的实现(3)"
继续ServiceProvider类
在之前的讲解中我们提到过Service类调用CreateCallSite方法时会递归调用,但是我们没具体说明如何递归调的。实际上Service类,通过反射创建实例的时候,会实例化的参数对象,而实例话参数对象通过ServiceProvider类创建,而ServiceProvider类创建参数的实例,又需要通过Service类(如果是通过Type注册的)创建。下面我们把Service的CreateInstanceCallSite方法以及ServiceProvider相关的方法列出来。
public IServiceCallSite CreateCallSite(ServiceProvider provider, ISet<Type> callSiteChain)
{
ConstructorInfo[] constructors = _descriptor.ImplementationType.GetTypeInfo()
.DeclaredConstructors
.Where(IsInjectable)
.ToArray();
// TODO: actual service-fulfillment constructor selection
if (constructors.Length == 1)
{
ParameterInfo[] parameters = constructors[0].GetParameters();
IServiceCallSite[] parameterCallSites = new IServiceCallSite[parameters.Length];
for (var index = 0; index != parameters.Length; ++index)
{
parameterCallSites[index] = provider.GetServiceCallSite(parameters[index].ParameterType, callSiteChain);
if (parameterCallSites[index] == null && parameters[index].HasDefaultValue)
{
parameterCallSites[index] = new ConstantCallSite(parameters[index].DefaultValue);
}
if (parameterCallSites[index] == null)
{
throw new InvalidOperationException(Resources.FormatCannotResolveService(
parameters[index].ParameterType,
_descriptor.ImplementationType));
}
}
return new ConstructorCallSite(constructors[0], parameterCallSites);
}
return new CreateInstanceCallSite(_descriptor);
}
Service的CreateCallSite
internal IServiceCallSite GetServiceCallSite(Type serviceType, ISet<Type> callSiteChain)
{
try
{
if (callSiteChain.Contains(serviceType))
{
throw new InvalidOperationException(Resources.FormatCircularDependencyException(serviceType));
}
callSiteChain.Add(serviceType);
ServiceEntry entry;
if (_table.TryGetEntry(serviceType, out entry))
{
return GetResolveCallSite(entry.Last, callSiteChain);
}
object emptyIEnumerableOrNull = GetEmptyIEnumerableOrNull(serviceType);
if (emptyIEnumerableOrNull != null)
{
return new EmptyIEnumerableCallSite(serviceType, emptyIEnumerableOrNull);
}
return null;
}
finally
{
callSiteChain.Remove(serviceType);
}
}
internal IServiceCallSite GetResolveCallSite(IService service, ISet<Type> callSiteChain)
{
IServiceCallSite serviceCallSite = service.CreateCallSite(this, callSiteChain);
if (service.Lifetime == ServiceLifetime.Transient)
{
return new TransientCallSite(serviceCallSite);
}
else if (service.Lifetime == ServiceLifetime.Scoped)
{
return new ScopedCallSite(service, serviceCallSite);
}
else
{
return new SingletonCallSite(service, serviceCallSite);
}
}
ServiceProvider
对于Service的CreateCallSite方法,之前我们已经介绍过,现在我们重点讲下ServiceProvider的GetServiceCallSite方法。从上面代码中我们发现参数中含有“ ISet<Type> callSiteChain”,这个参数是防止发生A的构造函数有B类型参数,B的构织函数中有A类型参数,当A,B都是通过类型注入的,那么系统会陷入死循环。而callSiteChain得作用就是防止这样的死循环发生,当创建A时,会在callSiteChain中查询历史中是否有A的创建过程,如果有则说明发生死循环了,直接抛出异常,结束;如果没有将A加入到callSiteChain中,继续创建其参数。GetResolveCallSite方法比较简单,对于ServiceProvider已经能够获取的IServiceCallSite实例,进行包装,已保证生成的实例能够适应不同的Scoped(该处应该使用设计模式中的代理模式,不过我设计模式不过关,请帮忙确认)。
对于TransientCallSite、ScopedCallSite、SingletonCallSite以及EmptyIEnumerableCallSite代码,如下所示:
private class EmptyIEnumerableCallSite : IServiceCallSite
{
private readonly object _serviceInstance;
private readonly Type _serviceType;
public EmptyIEnumerableCallSite(Type serviceType, object serviceInstance)
{
_serviceType = serviceType;
_serviceInstance = serviceInstance;
}
public object Invoke(ServiceProvider provider)
{
return _serviceInstance;
}
public Expression Build(Expression provider)
{
return Expression.Constant(_serviceInstance, _serviceType);
}
}
private class TransientCallSite : IServiceCallSite
{
private readonly IServiceCallSite _service;
public TransientCallSite(IServiceCallSite service)
{
_service = service;
}
public object Invoke(ServiceProvider provider)
{
return provider.CaptureDisposable(_service.Invoke(provider));
}
public Expression Build(Expression provider)
{
return Expression.Call(
provider,
CaptureDisposableMethodInfo,
_service.Build(provider));
}
}
private class ScopedCallSite : IServiceCallSite
{
private readonly IService _key;
private readonly IServiceCallSite _serviceCallSite;
public ScopedCallSite(IService key, IServiceCallSite serviceCallSite)
{
_key = key;
_serviceCallSite = serviceCallSite;
}
public virtual object Invoke(ServiceProvider provider)
{
object resolved;
lock (provider._sync)
{
if (!provider._resolvedServices.TryGetValue(_key, out resolved))
{
resolved = provider.CaptureDisposable(_serviceCallSite.Invoke(provider));
provider._resolvedServices.Add(_key, resolved);
}
}
return resolved;
}
public virtual Expression Build(Expression providerExpression)
{
var keyExpression = Expression.Constant(
_key,
typeof(IService));
var resolvedExpression = Expression.Variable(typeof(object), "resolved");
var resolvedServicesExpression = Expression.Field(
providerExpression,
"_resolvedServices");
var tryGetValueExpression = Expression.Call(
resolvedServicesExpression,
TryGetValueMethodInfo,
keyExpression,
resolvedExpression);
var captureDisposableExpression = Expression.Assign(
resolvedExpression,
Expression.Call(
providerExpression,
CaptureDisposableMethodInfo,
_serviceCallSite.Build(providerExpression)));
var addValueExpression = Expression.Call(
resolvedServicesExpression,
AddMethodInfo,
keyExpression,
resolvedExpression);
var blockExpression = Expression.Block(
typeof(object),
new[] { resolvedExpression },
Expression.IfThen(
Expression.Not(tryGetValueExpression),
Expression.Block(captureDisposableExpression, addValueExpression)),
resolvedExpression);
return Lock(providerExpression, blockExpression);
}
private static Expression Lock(Expression providerExpression, Expression body)
{
// The C# compiler would copy the lock object to guard against mutation.
// We don't, since we know the lock object is readonly.
var syncField = Expression.Field(providerExpression, "_sync");
var lockWasTaken = Expression.Variable(typeof(bool), "lockWasTaken");
var monitorEnter = Expression.Call(MonitorEnterMethodInfo, syncField, lockWasTaken);
var monitorExit = Expression.Call(MonitorExitMethodInfo, syncField);
var tryBody = Expression.Block(monitorEnter, body);
var finallyBody = Expression.IfThen(lockWasTaken, monitorExit);
return Expression.Block(
typeof(object),
new[] { lockWasTaken },
Expression.TryFinally(tryBody, finallyBody));
}
}
private class SingletonCallSite : ScopedCallSite
{
public SingletonCallSite(IService key, IServiceCallSite serviceCallSite) : base(key, serviceCallSite)
{
}
public override object Invoke(ServiceProvider provider)
{
return base.Invoke(provider._root);
}
public override Expression Build(Expression provider)
{
return base.Build(Expression.Field(provider, "_root"));
}
}