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