歡迎光臨
每天分享高質量文章

[Abp vNext 原始碼分析] – 3. 依賴註入與攔截器

一、簡要說明

ABP vNext 框架在使用依賴註入服務的時候,是直接使用的微軟提供的 Microsoft.Extensions.DependencyInjection 包。這裡與原來的 ABP 框架就不一樣了,原來的 ABP 框架還需要抽象出來一個 IIocManager 用來管理整個 IoC 容器,現在則直接操作 IServiceCollection 與 IServiceProvider 進行組件的註冊/解析。

這裡需要註意的是,雖然現在的依賴註入服務是使用微軟官方那一套庫進行操作,但是 ABP vNext 還是為我們提供了組件自動註冊、攔截器這些基礎功能。

二、原始碼分析

2.1 組件自動註冊

ABP vNext 仍然在其 Core 庫為我們提供了三種接口,即 ISingletonDependency 和 ITransientDependency 、IScopedDependency 接口,方便我們的型別/組件自動註冊,這三種接口分別對應了物件的 單例瞬時範圍 生命周期。只要任何型別/接口實現了以上任意接口,ABP vNext 就會在系統啟動時候,將這些物件註冊到 IoC 容器當中。

那麼究竟是在什麼時候呢?回顧上一章的模塊系統的文章,在模塊系統呼叫模塊的 ConfigureService() 的時候,就會有一個 services.AddAssembly(module.Type.Assembly) ,他會將模塊的所屬的程式集傳入。

public class ModuleLoader : IModuleLoader
{
    
    protected virtual void ConfigureServices(List 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);
        }
        
    }
    
}

看來核心就在於這個 AddAssembly() 擴展方法了,跳轉到方法的內部,發現真正幹事的是 IConventionalRegistrar 物件,暫且稱之為規約註冊器,而且我們可以擁有多個規約註冊器,你可以自己實現自動註冊規則。

public static IServiceCollection AddAssembly(this IServiceCollection services, Assembly assembly)
{
    
    foreach (var registrar in services.GetConventionalRegistrars())
    {
        registrar.AddAssembly(services, assembly);
    }

    return services;
}

該接口定義了三個方法,支持傳入程式集、型別陣列、具體型別,他們的預設實現都在抽象類 ConventionalRegistrarBase 當中。

public interface IConventionalRegistrar
{
    void AddAssembly(IServiceCollection services, Assembly assembly);

    void AddTypes(IServiceCollection services, params Type[] types);

    void AddType(IServiceCollection services, Type type);
}

抽象類當中的實現也非常簡單,他們最終都是呼叫的 AddType() 方法來將型別註冊到 IServiceCollection 當中的。

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);
}

所以我們的重點就在於 AddType() 方法,ABP vNext 框架預設的規約註冊器叫做 DefaultConventionalRegistrar,跳轉到其定義可以發現在其內部,除了對三種生命周期接口處理之外,如果型別使用了 DependencyAttribute 特性,也會根據該特性的引數配置進行不同的註冊邏輯。

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() 內部的 GetServiceLifetimeFromClassHierarcy() 方法確定了每個接口對應的生命周期。

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;
}

如果讀者有用過 AutoFac 或者 Castle Windsor 這些依賴註入框架的話,就知道我們要註冊一個型別,需要知道該型別的定義和實現。這裡的 AutoRegistrationHelper 工具類就會為我們確定註冊型別的型別定義,與其預設實現。

例如我有兩個接口 IDemoTestIDemoTestTwo,和他們的預設實現 DemoTest ,我可以有以下幾種方法來確定我的註冊型別。




[ExposeServices(typeof(IDemoTest),typeof(IDemoTestTwo))]
public class DemoTest : IDemoTest,ITransientDependency
{
    
}



public class DemoTest : IDemoTest,ITransientDependency
{

}



public class DemoTest : ITransientDependency
{

}

2.2 方法攔截器

2.2.1 ABP vNext 新的抽象層

在 ABP vNext 框架當中,將方法攔截器抽象了一層 IAbpInterceptor,但實際實現還是使用的 Castle.Core 所提供的動態代理功能,其定義在 Volo.Abp.Dependency.DynamicProxy 檔案夾當中,如下圖。

ABP vNext 將攔截器和方法呼叫模型都進行了定義,其中 AbpInterceptor 則是 IAbpInterceptor 的預設抽象實現。在ProxyHelper 工具類當中,提供了從代理物件獲取真實型別的方法。(PS: 通過 Castle.Core 代理後的物件與原有型別定義是不一致的。)


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 接口,則是封裝了一個被攔截方法呼叫時的各種引數,例如被攔截方法的在呼叫時所傳遞的引數,傳回值型別,方法定義等。而 ABP vNext 也為它建立了一個 CastleAbpMethodInvocationAdapter 配接器,實現了上述接口。

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 動態代理的集成

ABP vNext 在實際使用的時候,還是通過 Castle.Core 提供的動態代理功能來實現攔截器,相關的代碼存放在 Volo.Abp.Castle.Core 庫和 Volo.Abp.Autofac 庫當中。

首先我們來看 Castle.Core 庫對接口 IAbpMethodInvocation 和 IAbpInterceptor 的實現,在 CastleAbpInterceptorAdapter 中通過配接器來定義了一個標準的 Castle 攔截器,這個攔截器可以傳入 ABP vNext 定義的 IAbpInterceptor 作為其泛型引數。

public class CastleAbpInterceptorAdapter<TInterceptor> : IInterceptor
    where TInterceptor : IAbpInterceptor
{
    
}

Castle 的攔截器也會有一個 Intercept() 方法,該方法將在被攔截方法執行的時候觸發。在觸發之後,會根據當前方法的定義進行不同的操作,這裡異步方法和同步方法處理邏輯是不一樣的。

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);
    }
}

這裡我們以異步方法為例,其內部又會根據方法的傳回值是否是 Task 進行不同的操作,因為如果是泛型的 Task,說明該異步方法是有傳回值的,所以處理邏輯也不一樣。

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});
    }
}

進一步解析在傳回型別為 Task 時,它所呼叫的方法。

private async Task ExecuteWithoutReturnValueAsync(IInvocation invocation, IInvocationProceedInfo proceedInfo)
{
    
    await Task.Yield();

    
    await _abpInterceptor.InterceptAsync(
        new CastleAbpMethodInvocationAdapter(invocation, proceedInfo)
    );
}

從上述代碼可以得知,ABP vNext 的攔截器動作現在被包裹在一個 Castle 攔截器內部進行的。

那麼,我們的 Castle.Core 攔截器在什麼時候與型別進行系結的呢,每個攔截器又是如何與特性的型別進行註冊的呢?這裡我以審計日誌攔截器為例,看一下它在系統當中是如何註冊,並被使用的。

審計日誌相關的代碼存放在 Volo.Abp.Auditing 庫中,我們找到 AuditingInterceptor 型別,查看其定義可以看到它也是繼承自 AbpInterceptor 抽象基類。

public class AuditingInterceptor : AbpInterceptor, ITransientDependency
{
    
}

接著我們根據名字找到了攔截器的註冊工具類 AuditingInterceptorRegistrar,在型別的定義當中 ShouldIntercept() 與 ShouldAuditTypeByDefault() 根據傳入的 Type 型別,根據特定的邏輯決定是否為該型別關聯審計日誌攔截器。

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;
}

我們這裡需要關註的是 RegisterIfNeeded() 方法,它在審計日誌模塊的預加載方法就被添加到了一個 ServiceRegistrationActionList 集合當中,這個集合會在後面 AutoFac 進行型別註冊的時候被使用。

public static void RegisterIfNeeded(IOnServiceRegistredContext context)
{
    
    if (ShouldIntercept(context.ImplementationType))
    {
        context.Interceptors.TryAdd();
    }
}
public override void PreConfigureServices(ServiceConfigurationContext context)
{
    
    context.Services.OnRegistred(AuditingInterceptorRegistrar.RegisterIfNeeded);
}

繼續查看 OnRegistred() 的代碼,得到如下的定義,可以看到最後的 Action 會被添加到一個 ServiceRegistrationActionList 訪問器中。

public static void OnRegistred(this IServiceCollection services, Action 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>()?.Value;
    if (actionList == null)
    {
        actionList = new ServiceRegistrationActionList();
        services.AddObjectAccessor(actionList);
    }

    return actionList;
}

AutoFac 在執行註冊操作的時候,會呼叫 AutofacRegistration 靜態類的 Register 方法,該方法會遍歷整個 IServiceCollection 集合。在將型別註冊到 AutoFac 的 IoC 容器中的時候,在它的內部會呼叫 AbpRegistrationBuilderExtensions 提供的擴展方法為具體的型別添加過濾器。

private static void Register(
        ContainerBuilder builder,
        IServiceCollection services)
{
    var moduleContainer = services.GetSingletonInstance();
    
    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);
            }
            
        }
        
    }
}

下麵是擴展方法所定義的相關代碼,註意閱讀註釋。

public static IRegistrationBuilder ConfigureAbpConventions(
        this IRegistrationBuilder registrationBuilder,
        IModuleContainer moduleContainer,
        ServiceRegistrationActionList registrationActionList)
    where TActivatorData : ReflectionActivatorData
{
    
    registrationBuilder = registrationBuilder.InvokeRegistrationActions(registrationActionList, serviceType, implementationType);
    
}

private static IRegistrationBuilder InvokeRegistrationActions(this IRegistrationBuilder 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 AddInterceptors(
    this IRegistrationBuilder registrationBuilder,
    Type serviceType,
    IEnumerable interceptors)
    where TActivatorData : ReflectionActivatorData
{
    

    foreach (var interceptor in interceptors)
    {
        
        registrationBuilder.InterceptedBy(
            typeof(CastleAbpInterceptorAdapter<>).MakeGenericType(interceptor)
        );
    }

    return registrationBuilder;
}

2.3 物件訪問器

在第一章節的時候,我們就遇到過 IObjectAccessor 接口,基本上是針對該接口所提供的 Value 屬性進行操作,下麵就是該接口的定義和它的預設實現 ObjectAccessor,十分簡單,就一個泛型的 Value。

public interface IObjectAccessor<out T>
{
    [CanBeNull]
    T Value { get; }
}

public class ObjectAccessor<T> : IObjectAccessor<T>
{
    public T Value { get; set; }

    public ObjectAccessor()
    {
        
    }

    public ObjectAccessor([CanBeNull] T obj)
    {
        Value = obj;
    }
}

僅僅看上述的代碼,是看不出什麼名堂的,接著我們來到它的擴展方法定義 ServiceCollectionObjectAccessorExtensions

可以看到其核心的代碼在於 ObjectAccessor AddObjectAccessor(this IServiceCollection services, ObjectAccessor accessor)這個多載方法。它首先判斷某個特定泛型的物件訪問器是否被註冊,如果被註冊直接丟擲異常,沒有則繼續。

最後呢通過一個小技巧,將某個特定型別的物件訪問器作為單例註冊到 IoC 容器的頭部,方便快速檢索。

public static ObjectAccessor AddObjectAccessor(this IServiceCollection services, ObjectAccessor accessor)
{
    if (services.Any(s => s.ServiceType == typeof(ObjectAccessor)))
    {
        throw new Exception("An object accessor is registered before for type: " + typeof(T).AssemblyQualifiedName);
    }

    
    services.Insert(0, ServiceDescriptor.Singleton(typeof(ObjectAccessor), accessor));
    services.Insert(0, ServiceDescriptor.Singleton(typeof(IObjectAccessor), accessor));

    return accessor;
}

使用的時候,從第一章就有見到,這裡的物件訪問器可以傳入一個型別。這個時候其 Value 就是空的,但並不影響該型別的解析,只需要在真正使用之前將其 Value 值賦值為實體物件即可。

只是目前來看,該型別的作用並不是十分明顯,更多的時候是一個占位型別而已,你可以在任意時間替換某個型別的物件訪問器內部的 Value 值。

2.4 服務的範圍工廠

我們知道在依賴註入框架當中,有一種特別的生命周期叫做 Scoped 周期,這個周期在我之前的相關文章有講過,它是一個比較特別的生命周期。

簡單來說,Scoped 物件的生命周期只有在某個範圍內是單例存在的,例如以下偽代碼,用戶會請求 ScopedTest() 接口:

public class HomeController()
{
    public Task ScopedTest()
    {
        using(var scope = ScopedFactory.CreateScope())
        {
            scope.ChildContainer.Resolve.Name = "111";
            scope.ChildContainer.Resolve();
        }
    }
}

public class TestController()
{
    public TestController(TestApp app)
    {
        Console.WritleLine(app.Name);
    }
}

最後在 TestController 中,控制台會輸出 111 作為結果,在 HomeController 中 ScopedTest() 陳述句塊結束的時候,obj 物件會被釋放,在後續的請求當中,TestApp 都是作為一個 Scoped 物件生存的。

所以流程可以分為以下幾步:

  1. 通過 ScopeFactory 創建一個 Scope 範圍。
  2. 通過 Scope 範圍內的子容器,解析物件。
  3. 子容器在解析時,如果解析出來的型別是 Scope 生命周期,則在整個 Scope 存活期間,它都是單例的
  4. Scope 範圍釋放,會呼叫銷毀內部的子容器,並銷毀掉所有解析出來的物件。

在 Volo.Abp.Autofac 庫當中,定義了使用 AutoFac 封裝的範圍工廠與服務範圍型別的定義,他們將會作為預設的 IServiceScopeFactory 實現。

internal class AutofacServiceScopeFactory : IServiceScopeFactory
{
    private readonly ILifetimeScope _lifetimeScope;

    public AutofacServiceScopeFactory(ILifetimeScope lifetimeScope)
    {
        this._lifetimeScope = lifetimeScope;
    }

    public IServiceScope CreateScope()
    {
        return new AutofacServiceScope(this._lifetimeScope.BeginLifetimeScope());
    }
}

這裡可以看到,在構建這個工廠的時候,會註入一個 ILifetimScope,這個東西就是 AutoFac 提供的 子容器。在 CreateScope() 方法內部,我們通過構造一個 Scope 作為具體的範圍解析物件,並將子容器傳入到它的內部。

internal class AutofacServiceScope : IServiceScope
{
    private readonly ILifetimeScope _lifetimeScope;

    public AutofacServiceScope(ILifetimeScope lifetimeScope)
    {
        
        this._lifetimeScope = lifetimeScope;
        this.ServiceProvider = this._lifetimeScope.Resolve();
    }

    public IServiceProvider ServiceProvider { get; }

    public void Dispose()
    {
        
        this._lifetimeScope.Dispose();
    }
}

那麼是在什麼時候,我們的範圍工廠會被呼叫來構造一個 IServiceScope 物件呢?就是在 ASP.NET Core 每次請求的時候,它在獲得其內部的 RequestServices 時,就會通過 IServiceProvidersFeature 來創建一個 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;
    }
}

所以,我們在每次請求的時候,針對於 Scope 宣告周期的物件,預設的話都是在整個請求處理期間,都是單例的,除非顯式使用 using 陳述句塊宣告作用域。

而在 ABP vNext 中給我們提供了兩個 Scoped Factory,分別是 HttpContextServiceScopeFactory 和 DefaultServiceScopeFactory ,它們都繼承自 IHybridServiceScopeFactory 接口。

這個 IHybridServiceScopeFactory 接口只是一個空的接口,並繼承自 Microsoft Dependency Inject 提供的 IServiceScopeFactory 工廠接口。

但在實際註入的時候,並不會替換掉預設的 IServiceScopeFactory 實現。因為在 IHybridServiceScopeFactory 的預設兩個實現的定義上,他們都顯式得通過 ExposeServices 特性說明瞭自己是哪些型別的預設實現,且一般使用的時候,都是通過註入 IHybridServiceScopeFactory並結合 using 陳述句塊來操作。

例如在 Volo.Abp.Data 庫的 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);
        }
    }
}

只是這兩個實現有什麼不同呢?通過兩個型別的名字就可以看出來,一個是給 ASP.NET Core MVC 程式使用的,另一個則是預設的範圍工廠,下麵我們從代碼層面上來比較一下兩者之間的差別。

[ExposeServices(
    typeof(IHybridServiceScopeFactory),
    typeof(DefaultServiceScopeFactory)
    )]
public class DefaultServiceScopeFactory : IHybridServiceScopeFactory, ITransientDependency
{
    
    protected IServiceScopeFactory Factory { get; }

    public DefaultServiceScopeFactory(IServiceScopeFactory factory)
    {
        Factory = factory;
    }

    public IServiceScope CreateScope()
    {
        
        return Factory.CreateScope();
    }
}

HttpContextServiceScopeFactory 是放在 AspNetCore 模塊下的,從他的 Dependency 特性可以看出來,他會替換掉預設的 DefaultServiceScopeFactory 實現。

[ExposeServices(
    typeof(IHybridServiceScopeFactory),
    typeof(HttpContextServiceScopeFactory)
    )]
[Dependency(ReplaceServices = true)]
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 的時候,是使用的 HttpContext.RequestServices 作為這個 Scope 的解析器。

RequestServices, on the other hand, is a scoped container created from the root on each request.

翻譯成中文的意思就是,它是在每個請求的的時候創建的獨立範圍容器,其實就是開頭所說的子容器。

2.5 型別註冊完成的動作

其實這個玩意兒應該放在 2.2 節之前講,只是在寫完之後我才看到相關型別是放在依賴註入相關的檔案夾當中,這裡還請各位讀者理解一下。

早期在 Castle Windsor 當中,型別在註冊完成的時候會有一個註冊完成的事件,用戶可以掛載該事件來進行一些特殊的處理,比如說為型別添加動態代理。在 ABP vNext 當中因為支持多種不同的依賴註入框架,所以就沒有類似的事件來做處理。

ABP vNext 則封裝了一個 ServiceRegistrationActionList 型別,該型別用於儲存在型別註冊完成之後,用戶可以執行的操作,可以看到它就是一個 Action 集合,用於存放一系列回呼方法。

public class ServiceRegistrationActionList : List<Action<IOnServiceRegistredContext>>
{
    
}

由 2.2 節得知,這個玩意兒是在每一個型別註冊完成之後,都會被遍歷呼叫其中的 Action 動作。在呼叫的時候,會將當前註冊完成的型別封裝成一個 IOnServiceRegistredContext 物件,傳遞給具體的委托,這樣委托就能夠知道當前呼叫的型別,也就能夠將攔截器放在其 Interceptors 屬性當中了。

public interface IOnServiceRegistredContext
{
    ITypeList Interceptors { get; }

    Type ImplementationType { get; }
}

三、總結

ABP vNext 框架針對於依賴註入這塊的工作也進行了大量的精簡,就代碼量來說,比原有 ABP 框架減少了差不多一半左右,而且整個邏輯也比原來更加簡潔易懂。

開發人員在使用的時候,其實最多的是關註如何註入自己想要的型別。通過瞭解 ABP vNext 底層的代碼, 方便我們清楚攔截器和依賴註入框架的具體過程,這樣在後面擴展功能的時候才能夠做到心中有數。

已同步到看一看
赞(0)

分享創造快樂