Dora.Interception,爲.NET Core度身打造的AOP框架 [4]:與依賴注入框架的無縫集成

Dora.Interception最初的定位就是專門針對.NET Core的AOP框架,所以在整個迭代過程中我大部分是在做減法。對於.NET Core程序開發來說,依賴注入已經成爲無處不在並且“深入骨髓”的東西,不論是在進行業務應用的開發,還是進行基礎組件的開發,依賴注入是實現“鬆耦合”最爲理想的方式(沒有之一)。對於絕大部分AOP框架來說,它們最終都會體現爲創建一個能夠攔截的“代理對象”來實現對方法調用的攔截,但是.NET Core中針對服務實例的提供完全由通過IServiceProvider接口表示的DI容器來接管,所以Dora.Interception必須將兩者無縫地集成在一起。與依賴注入框架的集成不僅僅體現在對可被攔截的代理對象的創建,同樣應用在了針對攔截器的定義和註冊上。

一、IInterceptable<T>

由於.NET Core總是採用IServiceProvider接口表示的DI容器來提供注入的依賴服務對象,現在我們得將原始的目標對象轉換成能夠被攔截代理對象,爲此我們提供了一個泛型的服務接口IInterceptable<T>,它的Proxy屬性返回的就是這麼一個代理對象。

public interface IInterceptable<T> where T: class
{
    T Proxy { get; }
}

由於着了一個幫助我們提供可攔截代理的IInterceptable<T>服務,我們就可以在需要攔截目標類型的地方按照如下的方式注入該服務,並利用其Proxy屬性得到這個可被攔截的代理。

public class HomeController : Controller
{
    private readonly ISystemClock _clock;
    public HomeController(IInterceptable<ISystemClock> clockAccessor)
    {
        _clock = clockAccessor.Proxy;
        Debug.Assert(typeof(SystemClock) != _clock.GetType());
    }
}

二、讓IServiceProvider直接代理對象

在被依賴類型的構造函數中注入IInterceptable<T>服務的編程方式總顯得有點彆扭,這要求所有具有AOP需求的組件都需要依賴Dora.Interception,這無疑是不現實的。我們最終需要解決的還是如何讓IServiceProvider直接提供可被攔截的代理對象,爲此我對.NET Core依賴注入框架的源代碼作了一點很小的改動。這個經過簡單修改的IServiceProvider實現類型就是如下這個InterceptableServiceProvider 類型。至於具體修改了什麼,並不是一兩句話就能說清楚的,這涉及到整個依賴注入框架的設計,有興趣有查看源代碼。

internal sealed class InterceptableServiceProvider : IServiceProvider, IDisposable, IServiceProviderEngineCallback
{
    internal InterceptableServiceProvider(IEnumerable<ServiceDescriptor> serviceDescriptors, ServiceProviderOptions options, IInterceptingProxyFactory interceptingProxyFactory);
    public void Dispose();
    public object GetService(Type serviceType);
    void IServiceProviderEngineCallback.OnCreate(IServiceCallSite callSite);
    void IServiceProviderEngineCallback.OnResolve(Type serviceType, IServiceScope scope);
}

我們在Startup類型的ConfigureServices方法中,調用IServiceCollection的擴展方法BuildInterceptableServiceProvider創建的就是這麼一個InterceptableServiceProvider 對象。

public class Startup
{
    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        return services
            ...
            .BuildInterceptableServiceProvider();
    }
    ...
}

三、服務註冊

Dora.Interception所需的服務註冊都是通過調用IServiceCollection的擴展方法AddInterception來完成的,由於AddInterception會調整現有的服務註冊以支持上面介紹的IInterceptable<T>服務,所以AddInterception方法的調用需要放在所有服務註冊結束之後。創建InterceptableServiceProvider的BuildInterceptableServiceProvider方法內部會調用AddInterception方法,但是不會對現有的服務註冊作任何修改。

public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddInterception(this IServiceCollection services, Action<InterceptionBuilder> configure = null);
    public static IServiceProvider BuildInterceptableServiceProvider(this IServiceCollection services, Action<InterceptionBuilder> configure = null);
    public static IServiceProvider BuildInterceptableServiceProvider(this IServiceCollection services, bool validateScopes, Action<InterceptionBuilder> configure = null);
}

AddInterception和BuildInterceptableServiceProvider方法均定義了一個Action<InterceptionBuilder>類型的參數,我們可以利用它對註冊的服務做進一步定製。比如如果我們需要實現自定義的攔截器註冊方式,只需要將自定義的IInterceptorProviderResolver對象添加到InterceptorProviderResolvers 屬性表示的集合中即可。

public class InterceptionBuilder
{
    public InterceptionBuilder(IServiceCollection services);
    public InterceptorProviderResolverCollection InterceptorProviderResolvers { get; }
    public IServiceCollection Services { get; }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章