1. 程式人生 > >Dora.Interception,為.NET Core度身打造的AOP框架 [4]:與依賴註入框架的無縫集成

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

add type() style 約定 star sea 進行 builder bject

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

[1]:更加簡練的編程體驗
[2]:基於約定的攔截器定義方式
[3]:多樣性的攔截器應用方式
[4]:與依賴註入框架的深度整合
[5]:對攔截機制的靈活定制

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