[Abp vNext 源码分析] - 3. 依赖注入与拦截器
一、簡(jiǎn)要說(shuō)明
ABP vNext 框架在使用依賴注入服務(wù)的時(shí)候,是直接使用的微軟提供的?Microsoft.Extensions.DependencyInjection 包。這里與原來(lái)的 ABP 框架就不一樣了,原來(lái)的 ABP 框架還需要抽象出來(lái)一個(gè) IIocManager?用來(lái)管理整個(gè) IoC 容器,現(xiàn)在則直接操作?IServiceCollection?與?IServiceProvider 進(jìn)行組件的注冊(cè)/解析。
這里需要注意的是,雖然現(xiàn)在的依賴注入服務(wù)是使用微軟官方那一套庫(kù)進(jìn)行操作,但是 ABP vNext 還是為我們提供了組件自動(dòng)注冊(cè)、攔截器這些基礎(chǔ)功能。
二、源碼分析
2.1 組件自動(dòng)注冊(cè)
ABP vNext 仍然在其 Core 庫(kù)為我們提供了三種接口,即?ISingletonDependency?和?ITransientDependency?、IScopedDependency?接口,方便我們的類型/組件自動(dòng)注冊(cè),這三種接口分別對(duì)應(yīng)了對(duì)象的?單例、瞬時(shí)、范圍 生命周期。只要任何類型/接口實(shí)現(xiàn)了以上任意接口,ABP vNext 就會(huì)在系統(tǒng)啟動(dòng)時(shí)候,將這些對(duì)象注冊(cè)到 IoC 容器當(dāng)中。
那么究竟是在什么時(shí)候呢?回顧上一章的模塊系統(tǒng)的文章,在模塊系統(tǒng)調(diào)用模塊的 ConfigureService()?的時(shí)候,就會(huì)有一個(gè)?services.AddAssembly(module.Type.Assembly) ,他會(huì)將模塊的所屬的程序集傳入。
public class ModuleLoader : IModuleLoader{
protected virtual void ConfigureServices(List<IAbpModuleDescriptor> modules, IServiceCollection services)
{
foreach (var module in modules)
{
if (module.Instance is AbpModule abpModule)
{
if (!abpModule.SkipAutoServiceRegistration)
{
services.AddAssembly(module.Type.Assembly);
}
}
module.Instance.ConfigureServices(context);
}
}
}
看來(lái)核心就在于這個(gè)?AddAssembly()?擴(kuò)展方法了,跳轉(zhuǎn)到方法的內(nèi)部,發(fā)現(xiàn)真正干事的是?IConventionalRegistrar 對(duì)象,暫且稱之為規(guī)約注冊(cè)器,而且我們可以擁有多個(gè)規(guī)約注冊(cè)器,你可以自己實(shí)現(xiàn)自動(dòng)注冊(cè)規(guī)則。
public static IServiceCollection AddAssembly(this IServiceCollection services, Assembly assembly){
foreach (var registrar in services.GetConventionalRegistrars())
{
registrar.AddAssembly(services, assembly);
}
return services;
}
該接口定義了三個(gè)方法,支持傳入程序集、類型數(shù)組、具體類型,他們的默認(rèn)實(shí)現(xiàn)都在抽象類?ConventionalRegistrarBase 當(dāng)中。
public interface IConventionalRegistrar{
void AddAssembly(IServiceCollection services, Assembly assembly);
void AddTypes(IServiceCollection services, params Type[] types);
void AddType(IServiceCollection services, Type type);
}
抽象類當(dāng)中的實(shí)現(xiàn)也非常簡(jiǎn)單,他們最終都是調(diào)用的?AddType()?方法來(lái)將類型注冊(cè)到?IServiceCollection 當(dāng)中的。
public abstract class ConventionalRegistrarBase : IConventionalRegistrar{
public virtual void AddAssembly(IServiceCollection services, Assembly assembly)
{
var types = AssemblyHelper
.GetAllTypes(assembly)
.Where(
type => type != null &&
type.IsClass &&
!type.IsAbstract &&
!type.IsGenericType
).ToArray();
AddTypes(services, types);
}
public virtual void AddTypes(IServiceCollection services, params Type[] types)
{
foreach (var type in types)
{
AddType(services, type);
}
}
public abstract void AddType(IServiceCollection services, Type type);
}
所以我們的重點(diǎn)就在于?AddType()?方法,ABP vNext 框架默認(rèn)的規(guī)約注冊(cè)器叫做?DefaultConventionalRegistrar,跳轉(zhuǎn)到其定義可以發(fā)現(xiàn)在其內(nèi)部,除了對(duì)三種生命周期接口處理之外,如果類型使用了?DependencyAttribute?特性,也會(huì)根據(jù)該特性的參數(shù)配置進(jìn)行不同的注冊(cè)邏輯。
public override void AddType(IServiceCollection services, Type type){
if (IsConventionalRegistrationDisabled(type))
{
return;
}
var dependencyAttribute = GetDependencyAttributeOrNull(type);
var lifeTime = GetLifeTimeOrNull(type, dependencyAttribute);
if (lifeTime == null)
{
return;
}
foreach (var serviceType in AutoRegistrationHelper.GetExposedServices(services, type))
{
var serviceDescriptor = ServiceDescriptor.Describe(serviceType, type, lifeTime.Value);
if (dependencyAttribute?.ReplaceServices == true)
{
services.Replace(serviceDescriptor);
}
else if (dependencyAttribute?.TryRegister == true)
{
services.TryAdd(serviceDescriptor);
}
else
{
services.Add(serviceDescriptor);
}
}
}
這里就是在?GetLifeTimeOrNull()?內(nèi)部的?GetServiceLifetimeFromClassHierarcy() 方法確定了每個(gè)接口對(duì)應(yīng)的生命周期。
protected virtual ServiceLifetime? GetServiceLifetimeFromClassHierarcy(Type type){
if (typeof(ITransientDependency).GetTypeInfo().IsAssignableFrom(type))
{
return ServiceLifetime.Transient;
}
if (typeof(ISingletonDependency).GetTypeInfo().IsAssignableFrom(type))
{
return ServiceLifetime.Singleton;
}
if (typeof(IScopedDependency).GetTypeInfo().IsAssignableFrom(type))
{
return ServiceLifetime.Scoped;
}
return null;
}
如果讀者有用過(guò) AutoFac 或者 Castle Windsor 這些依賴注入框架的話,就知道我們要注冊(cè)一個(gè)類型,需要知道該類型的定義和實(shí)現(xiàn)。這里的 AutoRegistrationHelper 工具類就會(huì)為我們確定注冊(cè)類型的類型定義,與其默認(rèn)實(shí)現(xiàn)。
例如我有兩個(gè)接口?IDemoTest、IDemoTestTwo,和他們的默認(rèn)實(shí)現(xiàn)?DemoTest ,我可以有以下幾種方法來(lái)確定我的注冊(cè)類型。
[]
public class DemoTest : IDemoTest,ITransientDependency
{
}
public class DemoTest : IDemoTest,ITransientDependency
{
}
public class DemoTest : ITransientDependency
{
}
2.2 方法攔截器
2.2.1 ABP vNext 新的抽象層
在 ABP vNext 框架當(dāng)中,將方法攔截器抽象了一層?IAbpInterceptor,但實(shí)際實(shí)現(xiàn)還是使用的 Castle.Core 所提供的動(dòng)態(tài)代理功能,其定義在?Volo.Abp.Dependency.DynamicProxy 文件夾當(dāng)中,如下圖。
ABP vNext 將攔截器和方法調(diào)用模型都進(jìn)行了定義,其中?AbpInterceptor?則是?IAbpInterceptor 的默認(rèn)抽象實(shí)現(xiàn)。在ProxyHelper 工具類當(dāng)中,提供了從代理對(duì)象獲取真實(shí)類型的方法。(PS: 通過(guò) Castle.Core 代理后的對(duì)象與原有類型定義是不一致的。)
public interface IAbpInterceptor
{
void Intercept(IAbpMethodInvocation invocation);
Task InterceptAsync(IAbpMethodInvocation invocation);
}
public abstract class AbpInterceptor : IAbpInterceptor
{
public abstract void Intercept(IAbpMethodInvocation invocation);
public virtual Task InterceptAsync(IAbpMethodInvocation invocation)
{
Intercept(invocation);
return Task.CompletedTask;
}
}
至于?IAbpMethodInvocation 接口,則是封裝了一個(gè)被攔截方法調(diào)用時(shí)的各種參數(shù),例如被攔截方法的在調(diào)用時(shí)所傳遞的參數(shù),返回值類型,方法定義等。而 ABP vNext 也為它建立了一個(gè) CastleAbpMethodInvocationAdapter 適配器,實(shí)現(xiàn)了上述接口。
public interface IAbpMethodInvocation{
object[] Arguments { get; }
IReadOnlyDictionary<string, object> ArgumentsDictionary { get; }
Type[] GenericArguments { get; }
object TargetObject { get; }
MethodInfo Method { get; }
object ReturnValue { get; set; }
void Proceed();
Task ProceedAsync();
}
2.2.2 Castle.Core 動(dòng)態(tài)代理的集成
ABP vNext 在實(shí)際使用的時(shí)候,還是通過(guò) Castle.Core 提供的動(dòng)態(tài)代理功能來(lái)實(shí)現(xiàn)攔截器,相關(guān)的代碼存放在?Volo.Abp.Castle.Core?庫(kù)和?Volo.Abp.Autofac 庫(kù)當(dāng)中。
首先我們來(lái)看 Castle.Core 庫(kù)對(duì)接口?IAbpMethodInvocation?和?IAbpInterceptor?的實(shí)現(xiàn),在?CastleAbpInterceptorAdapter?中通過(guò)適配器來(lái)定義了一個(gè)標(biāo)準(zhǔn)的 Castle 攔截器,這個(gè)攔截器可以傳入 ABP vNext 定義的?IAbpInterceptor 作為其泛型參數(shù)。
public class CastleAbpInterceptorAdapter<TInterceptor> : IInterceptorwhere TInterceptor : IAbpInterceptor
{
}
Castle 的攔截器也會(huì)有一個(gè)?Intercept() 方法,該方法將在被攔截方法執(zhí)行的時(shí)候觸發(fā)。在觸發(fā)之后,會(huì)根據(jù)當(dāng)前方法的定義進(jìn)行不同的操作,這里異步方法和同步方法處理邏輯是不一樣的。
public void Intercept(IInvocation invocation){
var proceedInfo = invocation.CaptureProceedInfo();
var method = invocation.MethodInvocationTarget ?? invocation.Method;
if (method.IsAsync())
{
InterceptAsyncMethod(invocation, proceedInfo);
}
else
{
InterceptSyncMethod(invocation, proceedInfo);
}
}
這里我們以異步方法為例,其內(nèi)部又會(huì)根據(jù)方法的返回值是否是 Task 進(jìn)行不同的操作,因?yàn)槿绻欠盒偷?Task,說(shuō)明該異步方法是有返回值的,所以處理邏輯也不一樣。
private void InterceptAsyncMethod(IInvocation invocation, IInvocationProceedInfo proceedInfo){
if (invocation.Method.ReturnType == typeof(Task))
{
invocation.ReturnValue = MethodExecuteWithoutReturnValueAsync
.Invoke(this, new object[] { invocation, proceedInfo });
}
else
{
invocation.ReturnValue = MethodExecuteWithReturnValueAsync
.MakeGenericMethod(invocation.Method.ReturnType.GenericTypeArguments[0])
.Invoke(this, new object[] {invocation, proceedInfo});
}
}
進(jìn)一步解析在返回類型為?Task 時(shí),它所調(diào)用的方法。
private async Task ExecuteWithoutReturnValueAsync(IInvocation invocation, IInvocationProceedInfo proceedInfo){
await Task.Yield();
await _abpInterceptor.InterceptAsync(
new CastleAbpMethodInvocationAdapter(invocation, proceedInfo)
);
}
從上述代碼可以得知,ABP vNext 的攔截器動(dòng)作現(xiàn)在被包裹在一個(gè) Castle 攔截器內(nèi)部進(jìn)行的。
那么,我們的 Castle.Core 攔截器在什么時(shí)候與類型進(jìn)行綁定的呢,每個(gè)攔截器又是如何與特性的類型進(jìn)行注冊(cè)的呢?這里我以審計(jì)日志攔截器為例,看一下它在系統(tǒng)當(dāng)中是如何注冊(cè),并被使用的。
審計(jì)日志相關(guān)的代碼存放在?Volo.Abp.Auditing?庫(kù)中,我們找到?AuditingInterceptor?類型,查看其定義可以看到它也是繼承自?AbpInterceptor 抽象基類。
public class AuditingInterceptor : AbpInterceptor, ITransientDependency{
}
接著我們根據(jù)名字找到了攔截器的注冊(cè)工具類?AuditingInterceptorRegistrar,在類型的定義當(dāng)中?ShouldIntercept()?與?ShouldAuditTypeByDefault() 根據(jù)傳入的 Type 類型,根據(jù)特定的邏輯決定是否為該類型關(guān)聯(lián)審計(jì)日志攔截器。
private static bool ShouldIntercept(Type type){
if (ShouldAuditTypeByDefault(type))
{
return true;
}
if (type.GetMethods().Any(m => m.IsDefined(typeof(AuditedAttribute), true)))
{
return true;
}
return false;
}
public static bool ShouldAuditTypeByDefault(Type type)
{
if (type.IsDefined(typeof(AuditedAttribute), true))
{
return true;
}
if (type.IsDefined(typeof(DisableAuditingAttribute), true))
{
return false;
}
if (typeof(IAuditingEnabled).IsAssignableFrom(type))
{
return true;
}
return false;
}
我們這里需要關(guān)注的是?RegisterIfNeeded()?方法,它在審計(jì)日志模塊的預(yù)加載方法就被添加到了一個(gè)?ServiceRegistrationActionList 集合當(dāng)中,這個(gè)集合會(huì)在后面 AutoFac 進(jìn)行類型注冊(cè)的時(shí)候被使用。
public static void RegisterIfNeeded(IOnServiceRegistredContext context){
if (ShouldIntercept(context.ImplementationType))
{
context.Interceptors.TryAdd<AuditingInterceptor>();
}
}public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.OnRegistred(AuditingInterceptorRegistrar.RegisterIfNeeded);
}
繼續(xù)查看?OnRegistred()?的代碼,得到如下的定義,可以看到最后的 Action 會(huì)被添加到一個(gè)?ServiceRegistrationActionList 訪問(wèn)器中。
public static void OnRegistred(this IServiceCollection services, Action<IOnServiceRegistredContext> registrationAction){
GetOrCreateRegistrationActionList(services).Add(registrationAction);
}
public static ServiceRegistrationActionList GetRegistrationActionList(this IServiceCollection services)
{
return GetOrCreateRegistrationActionList(services);
}
private static ServiceRegistrationActionList GetOrCreateRegistrationActionList(IServiceCollection services)
{
var actionList = services.GetSingletonInstanceOrNull<IObjectAccessor<ServiceRegistrationActionList>>()?.Value;
if (actionList == null)
{
actionList = new ServiceRegistrationActionList();
services.AddObjectAccessor(actionList);
}
return actionList;
}
AutoFac 在執(zhí)行注冊(cè)操作的時(shí)候,會(huì)調(diào)用?AutofacRegistration?靜態(tài)類的?Register?方法,該方法會(huì)遍歷整個(gè)?IServiceCollection 集合。在將類型注冊(cè)到 AutoFac 的 IoC 容器中的時(shí)候,在它的內(nèi)部會(huì)調(diào)用 AbpRegistrationBuilderExtensions 提供的擴(kuò)展方法為具體的類型添加過(guò)濾器。
private static void Register(ContainerBuilder builder,
IServiceCollection services)
{
var moduleContainer = services.GetSingletonInstance<IModuleContainer>();
var registrationActionList = services.GetRegistrationActionList();
foreach (var service in services)
{
if (service.ImplementationType != null)
{
var serviceTypeInfo = service.ServiceType.GetTypeInfo();
if (serviceTypeInfo.IsGenericTypeDefinition)
{
builder
.RegisterGeneric(service.ImplementationType)
.As(service.ServiceType)
.ConfigureLifecycle(service.Lifetime)
.ConfigureAbpConventions(moduleContainer, registrationActionList);
}
}
}
}
下面是擴(kuò)展方法所定義的相關(guān)代碼,注意閱讀注釋。
public static IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> ConfigureAbpConventions<TLimit, TActivatorData, TRegistrationStyle>(this IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> registrationBuilder,
IModuleContainer moduleContainer,
ServiceRegistrationActionList registrationActionList)
where TActivatorData : ReflectionActivatorData
{
registrationBuilder = registrationBuilder.InvokeRegistrationActions(registrationActionList, serviceType, implementationType);
}
private static IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> InvokeRegistrationActions<TLimit, TActivatorData, TRegistrationStyle>(this IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> registrationBuilder, ServiceRegistrationActionList registrationActionList, Type serviceType, Type implementationType)
where TActivatorData : ReflectionActivatorData
{
var serviceRegistredArgs = new OnServiceRegistredContext(serviceType, implementationType);
foreach (var registrationAction in registrationActionList)
{
registrationAction.Invoke(serviceRegistredArgs);
}
if (serviceRegistredArgs.Interceptors.Any())
{
registrationBuilder = registrationBuilder.AddInterceptors(
serviceType,
serviceRegistredArgs.Interceptors
);
}
return registrationBuilder;
}
private static IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> AddInterceptors<TLimit, TActivatorData, TRegistrationStyle>(
this IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> registrationBuilder,
Type serviceType,
IEnumerable<Type> interceptors)
where TActivatorData : ReflectionActivatorData
{
foreach (var interceptor in interceptors)
{
registrationBuilder.InterceptedBy(
typeof(CastleAbpInterceptorAdapter<>).MakeGenericType(interceptor)
);
}
return registrationBuilder;
}
2.3 對(duì)象訪問(wèn)器
在第一章節(jié)的時(shí)候,我們就遇到過(guò)?IObjectAccessor<T>?接口,基本上是針對(duì)該接口所提供的?Value?屬性進(jìn)行操作,下面就是該接口的定義和它的默認(rèn)實(shí)現(xiàn)?ObjectAccessor<T>,十分簡(jiǎn)單,就一個(gè)泛型的 Value。
public interface IObjectAccessor<out T>{
[]
T Value { get; }
}
public class ObjectAccessor<T> : IObjectAccessor<T>
{
public T Value { get; set; }
public ObjectAccessor()
{
}
public ObjectAccessor([CanBeNull] T obj)
{
Value = obj;
}
}
僅僅看上述的代碼,是看不出什么名堂的,接著我們來(lái)到它的擴(kuò)展方法定義?ServiceCollectionObjectAccessorExtensions 。
可以看到其核心的代碼在于?ObjectAccessor<T> AddObjectAccessor<T>(this IServiceCollection services, ObjectAccessor<T> accessor)這個(gè)重載方法。它首先判斷某個(gè)特定泛型的對(duì)象訪問(wèn)器是否被注冊(cè),如果被注冊(cè)直接拋出異常,沒(méi)有則繼續(xù)。
最后呢通過(guò)一個(gè)小技巧,將某個(gè)特定類型的對(duì)象訪問(wèn)器作為單例注冊(cè)到 IoC 容器的頭部,方便快速檢索。
public static ObjectAccessor<T> AddObjectAccessor<T>(this IServiceCollection services, ObjectAccessor<T> accessor){
if (services.Any(s => s.ServiceType == typeof(ObjectAccessor<T>)))
{
throw new Exception("An object accessor is registered before for type: " + typeof(T).AssemblyQualifiedName);
}
services.Insert(0, ServiceDescriptor.Singleton(typeof(ObjectAccessor<T>), accessor));
services.Insert(0, ServiceDescriptor.Singleton(typeof(IObjectAccessor<T>), accessor));
return accessor;
}
使用的時(shí)候,從第一章就有見(jiàn)到,這里的對(duì)象訪問(wèn)器可以傳入一個(gè)類型。這個(gè)時(shí)候其 Value 就是空的,但并不影響該類型的解析,只需要在真正使用之前將其 Value 值賦值為實(shí)例對(duì)象即可。
只是目前來(lái)看,該類型的作用并不是十分明顯,更多的時(shí)候是一個(gè)占位類型而已,你可以在任意時(shí)間替換某個(gè)類型的對(duì)象訪問(wèn)器內(nèi)部的 Value 值。
2.4 服務(wù)的范圍工廠
我們知道在依賴注入框架當(dāng)中,有一種特別的生命周期叫做 Scoped 周期,這個(gè)周期在我之前的相關(guān)文章有講過(guò),它是一個(gè)比較特別的生命周期。
簡(jiǎn)單來(lái)說(shuō),Scoped 對(duì)象的生命周期只有在某個(gè)范圍內(nèi)是單例存在的,例如以下偽代碼,用戶會(huì)請(qǐng)求?ScopedTest() 接口:
public class HomeController(){
public Task ScopedTest()
{
using(var scope = ScopedFactory.CreateScope<TestApp>())
{
scope.ChildContainer.Resolve<TestApp>.Name = "111";
scope.ChildContainer.Resolve<TestController>();
}
}
}
public class TestController()
{
public TestController(TestApp app)
{
Console.WritleLine(app.Name);
}
}
最后在 TestController 中,控制臺(tái)會(huì)輸出?111?作為結(jié)果,在 HomeController 中?ScopedTest() 語(yǔ)句塊結(jié)束的時(shí)候,obj 對(duì)象會(huì)被釋放,在后續(xù)的請(qǐng)求當(dāng)中,TestApp 都是作為一個(gè) Scoped 對(duì)象生存的。
所以流程可以分為以下幾步:
通過(guò) ScopeFactory 創(chuàng)建一個(gè) Scope 范圍。
通過(guò) Scope 范圍內(nèi)的子容器,解析對(duì)象。
子容器在解析時(shí),如果解析出來(lái)的類型是 Scope 生命周期,則在整個(gè) Scope 存活期間,它都是單例的。
Scope 范圍釋放,會(huì)調(diào)用銷毀內(nèi)部的子容器,并銷毀掉所有解析出來(lái)的對(duì)象。
在?Volo.Abp.Autofac?庫(kù)當(dāng)中,定義了使用 AutoFac 封裝的范圍工廠與服務(wù)范圍類型的定義,他們將會(huì)作為默認(rèn)的?IServiceScopeFactory 實(shí)現(xiàn)。
internal class AutofacServiceScopeFactory : IServiceScopeFactory{
private readonly ILifetimeScope _lifetimeScope;
public AutofacServiceScopeFactory(ILifetimeScope lifetimeScope)
{
this._lifetimeScope = lifetimeScope;
}
public IServiceScope CreateScope()
{
return new AutofacServiceScope(this._lifetimeScope.BeginLifetimeScope());
}
}
這里可以看到,在構(gòu)建這個(gè)工廠的時(shí)候,會(huì)注入一個(gè)?ILifetimScope,這個(gè)東西就是 AutoFac 提供的?子容器。在 CreateScope() 方法內(nèi)部,我們通過(guò)構(gòu)造一個(gè) Scope 作為具體的范圍解析對(duì)象,并將子容器傳入到它的內(nèi)部。
internal class AutofacServiceScope : IServiceScope{
private readonly ILifetimeScope _lifetimeScope;
public AutofacServiceScope(ILifetimeScope lifetimeScope)
{
this._lifetimeScope = lifetimeScope;
this.ServiceProvider = this._lifetimeScope.Resolve<IServiceProvider>();
}
public IServiceProvider ServiceProvider { get; }
public void Dispose()
{
this._lifetimeScope.Dispose();
}
}
那么是在什么時(shí)候,我們的范圍工廠會(huì)被調(diào)用來(lái)構(gòu)造一個(gè)?IServiceScope 對(duì)象呢?就是在 ASP.NET Core 每次請(qǐng)求的時(shí)候,它在獲得其內(nèi)部的 RequestServices?時(shí),就會(huì)通過(guò)?IServiceProvidersFeature 來(lái)創(chuàng)建一個(gè) Scope 范圍。
public IServiceProvider RequestServices{
get
{
if (!_requestServicesSet)
{
_context.Response.RegisterForDispose(this);
_scope = _scopeFactory.CreateScope();
_requestServices = _scope.ServiceProvider;
_requestServicesSet = true;
}
return _requestServices;
}
set
{
_requestServices = value;
_requestServicesSet = true;
}
}
所以,我們?cè)诿看握?qǐng)求的時(shí)候,針對(duì)于 Scope 聲明周期的對(duì)象,默認(rèn)的話都是在整個(gè)請(qǐng)求處理期間,都是單例的,除非顯式使用?using 語(yǔ)句塊聲明作用域。
而在 ABP vNext 中給我們提供了兩個(gè) Scoped Factory,分別是?HttpContextServiceScopeFactory?和?DefaultServiceScopeFactory?,它們都繼承自?IHybridServiceScopeFactory 接口。
這個(gè)?IHybridServiceScopeFactory?接口只是一個(gè)空的接口,并繼承自 Microsoft Dependency Inject 提供的?IServiceScopeFactory 工廠接口。
但在實(shí)際注入的時(shí)候,并不會(huì)替換掉默認(rèn)的?IServiceScopeFactory 實(shí)現(xiàn)。因?yàn)樵?IHybridServiceScopeFactory?的默認(rèn)兩個(gè)實(shí)現(xiàn)的定義上,他們都顯式得通過(guò)?ExposeServices?特性說(shuō)明了自己是哪些類型的默認(rèn)實(shí)現(xiàn),且一般使用的時(shí)候,都是通過(guò)注入?IHybridServiceScopeFactory并結(jié)合?using 語(yǔ)句塊來(lái)操作。
例如在?Volo.Abp.Data?庫(kù)的?DataSeeder 類型中,有如下用法。
public async Task SeedAsync(DataSeedContext context){
using (var scope = ServiceScopeFactory.CreateScope())
{
foreach (var contributorType in Options.Contributors)
{
var contributor = (IDataSeedContributor) scope
.ServiceProvider
.GetRequiredService(contributorType);
await contributor.SeedAsync(context);
}
}
}
只是這兩個(gè)實(shí)現(xiàn)有什么不同呢?通過(guò)兩個(gè)類型的名字就可以看出來(lái),一個(gè)是給 ASP.NET Core MVC 程序使用的,另一個(gè)則是默認(rèn)的范圍工廠,下面我們從代碼層面上來(lái)比較一下兩者之間的差別。
[]public class DefaultServiceScopeFactory : IHybridServiceScopeFactory, ITransientDependency
{
protected IServiceScopeFactory Factory { get; }
public DefaultServiceScopeFactory(IServiceScopeFactory factory)
{
Factory = factory;
}
public IServiceScope CreateScope()
{
return Factory.CreateScope();
}
}
HttpContextServiceScopeFactory?是放在 AspNetCore 模塊下的,從他的?Dependency?特性可以看出來(lái),他會(huì)替換掉默認(rèn)的?DefaultServiceScopeFactory 實(shí)現(xiàn)。
[][]
public class HttpContextServiceScopeFactory : IHybridServiceScopeFactory, ITransientDependency
{
protected IHttpContextAccessor HttpContextAccessor { get; }
protected IServiceScopeFactory ServiceScopeFactory { get; }
public HttpContextServiceScopeFactory(
IHttpContextAccessor httpContextAccessor,
IServiceScopeFactory serviceScopeFactory)
{
HttpContextAccessor = httpContextAccessor;
ServiceScopeFactory = serviceScopeFactory;
}
public virtual IServiceScope CreateScope()
{
var httpContext = HttpContextAccessor.HttpContext;
if (httpContext == null)
{
return ServiceScopeFactory.CreateScope();
}
return new NonDisposedHttpContextServiceScope(httpContext.RequestServices);
}
protected class NonDisposedHttpContextServiceScope : IServiceScope
{
public IServiceProvider ServiceProvider { get; }
public NonDisposedHttpContextServiceScope(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
}
public void Dispose()
{
}
}
}
可以看到,后者如果在 HttpContext 不為 null 的時(shí)候,是使用的?HttpContext.RequestServices 作為這個(gè) Scope 的解析器。
RequestServices, on the other hand, is a?scoped?container created from the root on each request.
翻譯成中文的意思就是,它是在每個(gè)請(qǐng)求的的時(shí)候創(chuàng)建的獨(dú)立范圍容器,其實(shí)就是開(kāi)頭所說(shuō)的子容器。
2.5 類型注冊(cè)完成的動(dòng)作
其實(shí)這個(gè)玩意兒應(yīng)該放在 2.2 節(jié)之前講,只是在寫(xiě)完之后我才看到相關(guān)類型是放在依賴注入相關(guān)的文件夾當(dāng)中,這里還請(qǐng)各位讀者理解一下。
早期在 Castle Windsor 當(dāng)中,類型在注冊(cè)完成的時(shí)候會(huì)有一個(gè)注冊(cè)完成的事件,用戶可以掛載該事件來(lái)進(jìn)行一些特殊的處理,比如說(shuō)為類型添加動(dòng)態(tài)代理。在 ABP vNext 當(dāng)中因?yàn)橹С侄喾N不同的依賴注入框架,所以就沒(méi)有類似的事件來(lái)做處理。
ABP vNext 則封裝了一個(gè)?ServiceRegistrationActionList 類型,該類型用于存儲(chǔ)在類型注冊(cè)完成之后,用戶可以執(zhí)行的操作,可以看到它就是一個(gè) Action 集合,用于存放一系列回調(diào)方法。
public class ServiceRegistrationActionList : List<Action<IOnServiceRegistredContext>>{
}
由 2.2 節(jié)得知,這個(gè)玩意兒是在每一個(gè)類型注冊(cè)完成之后,都會(huì)被遍歷調(diào)用其中的 Action 動(dòng)作。在調(diào)用的時(shí)候,會(huì)將當(dāng)前注冊(cè)完成的類型封裝成一個(gè) IOnServiceRegistredContext?對(duì)象,傳遞給具體的委托,這樣委托就能夠知道當(dāng)前調(diào)用的類型,也就能夠?qū)r截器放在其?Interceptors 屬性當(dāng)中了。
public interface IOnServiceRegistredContext{
ITypeList<IAbpInterceptor> Interceptors { get; }
Type ImplementationType { get; }
}
三、總結(jié)
ABP vNext 框架針對(duì)于依賴注入這塊的工作也進(jìn)行了大量的精簡(jiǎn),就代碼量來(lái)說(shuō),比原有 ABP 框架減少了差不多一半左右,而且整個(gè)邏輯也比原來(lái)更加簡(jiǎn)潔易懂。
開(kāi)發(fā)人員在使用的時(shí)候,其實(shí)最多的是關(guān)注如何注入自己想要的類型。通過(guò)了解 ABP vNext 底層的代碼, 方便我們清楚攔截器和依賴注入框架的具體過(guò)程,這樣在后面擴(kuò)展功能的時(shí)候才能夠做到心中有數(shù)。
原文地址:https://www.cnblogs.com/myzony/p/10755010.html
.NET社區(qū)新聞,深度好文,歡迎訪問(wèn)公眾號(hào)文章匯總?http://www.csharpkit.com?
總結(jié)
以上是生活随笔為你收集整理的[Abp vNext 源码分析] - 3. 依赖注入与拦截器的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 如何在ASP.NET Core中使用Az
- 下一篇: dotnet core 微服务教程