21

Dora.Interception,为.NET Core度身打造的AOP框架 [4]:与依赖注入框架的无缝集成

 5 years ago
source link: http://www.cnblogs.com/artech/p/dora-interception-04.html?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK