全新升级的AOP框架Dora.Interception[5]: 实现任意的拦截器注册方式
Dora.Interception(github地址,觉得不错不妨给一颗星)提供了两种拦截器注册方式,一种是利用标注在目标类型、属性和方法上的InterceptorAttribute特性,另一种采用基于目标方法或者属性的调用表达式。通过提供的扩展点,我们可以任何我们希望的拦截器注册方式。(拙著《ASP.NET Core 6框架揭秘》)、
目录
一、IInterceptorProvider
二、InterceptorProviderBase
三、实现一种“万能”的拦截器注册方式
四、ConditionalInterceptorProvider
一、IInterceptorProvider
拦截器最终需要应用到某个具体的目标方法上,所以拦截器的注册就是如何建立拦截器与目标方法之间的映射关系,Dora.Interception将这一功能体现在如下所示的IInterceptorProvider接口上。顾名思义,IInterceptorProvider旨在解决为某个类型的某个方法提供拦截器列表的问题,这一个功能体现在GetInterceptors方法上。如下面的代码片段所示,该方法返回一组Sortable
public interface IInterceptorProvider { bool CanIntercept(Type targetType, MethodInfo method, out bool suppressed); IEnumerable> GetInterceptors(Type targetType, MethodInfo method); void Validate(Type targetType, Action methodValidator, Action propertyValidator) {} } public sealed class Sortable { public int Order { get; } public T Value { get; set; } public Sortable(int order, T value) { Order = order; Value = value; } }
除了GetInterceptors方法,IInterceptorProvider接口还定义了额外两个方法,CanIntercept方法用来判断指定的方式是否需要被拦截,代码生成器会利用这个方法决定如果生成最终可供拦截的代理类。另一个Validate方法用来验证针对指定类型的拦截器注册方式是否合法,即拦截器是否应用到一些根本无法被拦截的方法或者属性上,具体的检验逻辑由方法提供的两个委托来完成。
二、InterceptorProviderBase
我们自定义的IInterceptorProvider实现类型一般派生于如下这个抽象基类InterceptorProviderBase,后者在接口的基础上提供了一个IConventionalInterceptorFactory接口类型的InterceptorFactory属性。顾名思义,IConventionalInterceptorFactory对象帮助我们按照约定定义的拦截器类型或者其实例转换成标准的拦截器表现形式,即InvokeDelegate委托。
public abstract class InterceptorProviderBase : IInterceptorProvider { public IConventionalInterceptorFactory InterceptorFactory { get; } protected InterceptorProviderBase(IConventionalInterceptorFactory interceptorFactory) ; public abstract bool CanIntercept(Type targetType, MethodInfo method, out bool suppressed); public abstract IEnumerable> GetInterceptors(Type targetType, MethodInfo method); } public interface IConventionalInterceptorFactory { InvokeDelegate CreateInterceptor(Type interceptorType, params object[] arguments); InvokeDelegate CreateInterceptor(object interceptor); }
三、实现一种“万能”的拦截器注册方式
接下来我们通过自定义的IInterceptorProvider类型实现一种“万能”的拦截器注册方式——根据指定的条件表达式将指定的拦截器关联到目标方法上。在提供具体实现之前,我们先来体验一下由它达成的编程模型。
public class FoobarInterceptor { public ValueTask InvokeAsync(InvocationContext invocationContext) { var method = invocationContext.MethodInfo; Console.WriteLine($"{method.DeclaringType!.Name}.{method.Name} is intercepted."); return invocationContext.ProceedAsync(); } } public class Foobar { public virtual void M() { } public virtual object? P { get; set; } }
我们依然以上面这个简单的拦截器类型FoobarInterceptor为例,现在我们需要将它应用到Foobar类型的M和P属性的Set方法上,针对FoobarInterceptor的注册就可以按照如下方式来完成。如代码片段所示,我们在调用InterceptionBuilder的RegisterInterceptors扩展方法中提供了一个Action
var foobar= new ServiceCollection() .AddSingleton() .BuildInterceptableServiceProvider(interception => interception.RegisterInterceptors(RegisterInterceptors)) .GetRequiredService (); foobar.M(); _ = foobar.P; foobar.P = null; Console.ReadLine(); static void RegisterInterceptors(ConditionalInterceptorProviderOptions options) { options.For () .To(1, (type, method) => type == typeof(Foobar) && method.Name == "M") .To(1, (type, method) => type == typeof(Foobar) && method.IsSpecialName && method.Name == "set_P"); }
程序运行后会在控制台输出如下的结果,可以看出FoobarInterceptor拦截确实只应用到M和P属性的Set方法上,属性的Get方法并未被拦截。
四、ConditionalInterceptorProvider
上述这种针对匹配条件的“万能”注册方式是通过如下这个ConditionalInterceptorProvider类型实现的。ConditionalInterceptorProviderOptions类型定义了对应的配置选项,其核心就是一组ConditionalInterceptorRegistration对象的集合,而每一个ConditionalInterceptorRegistration对象是一个表示匹配条件的Func
public class ConditionalInterceptorProvider : InterceptorProviderBase { private readonly ConditionalInterceptorProviderOptions _options; public ConditionalInterceptorProvider(IConventionalInterceptorFactory interceptorFactory, IOptionsoptionsAccessor) : base(interceptorFactory) => _options = optionsAccessor.Value; public override bool CanIntercept(Type targetType, MethodInfo method, out bool suppressed) { suppressed = false; return _options.Registrations.Any(it => it.Condition(targetType, method)); } public override IEnumerable > GetInterceptors(Type targetType, MethodInfo method) => _options.Registrations.Where(it => it.Condition(targetType, method)).Select(it => it.Factory(InterceptorFactory)).ToList(); } public class ConditionalInterceptorProviderOptions { public IList Registrations { get; } = new List (); public Registry For (params object[] arguments)=> new(factory => factory.CreateInterceptor(typeof(TInterceptor), arguments), this); } public class Registry { private readonly Func _factory; private readonly ConditionalInterceptorProviderOptions _options; public Registry(Func factory, ConditionalInterceptorProviderOptions options) { _factory = factory; _options = options; } public Registry To(int order, Func bool> condition) { var entry = new ConditionalInterceptorRegistration(condition, factory=>new Sortable (order, _factory(factory))); _options.Registrations.Add(entry); return this; } } public class ConditionalInterceptorRegistration { public Func bool> Condition { get; } public Func > Factory { get; } public ConditionalInterceptorRegistration(Func bool> condition, Func > factory) { Condition = condition; Factory = factory; } }
这一组映射关系利用ConditionalInterceptorProviderOptions的For