

全新升级的AOP框架Dora.Interception[6]: 实现任意的拦截器注册方式 - Artech
source link: https://www.cnblogs.com/artech/p/dora-aop-5x.html
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提供了两种拦截器注册方式,一种是利用标注在目标类型、属性和方法上的InterceptorAttribute特性,另一种采用基于目标方法或者属性的调用表达式。通过提供的扩展点,我们可以任何我们希望的拦截器注册方式。(拙著《ASP.NET Core 6框架揭秘》6折优惠,首印送签名专属书签)
一、IInterceptorProvider
拦截器最终需要应用到某个具体的目标方法上,所以拦截器的注册就是如何建立拦截器与目标方法之间的映射关系,Dora.Interception将这一功能体现在如下所示的IInterceptorProvider接口上。顾名思义,IInterceptorProvider旨在解决为某个类型的某个方法提供拦截器列表的问题,这一个功能体现在GetInterceptors方法上。如下面的代码片段所示,该方法返回一组Sortable<InvokeDelegate>对象,InvokeDelegate代表拦截器本身,Sortable<InvokeDelegate>对象在此基础上添加了必要排序元素。
public interface IInterceptorProvider { bool CanIntercept(Type targetType, MethodInfo method, out bool suppressed); IEnumerable<Sortable<InvokeDelegate>> GetInterceptors(Type targetType, MethodInfo method); void Validate(Type targetType, Action<MethodInfo> methodValidator, Action<PropertyInfo> propertyValidator) {} } public sealed class Sortable<T> { 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<Sortable<InvokeDelegate>> 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<ConditionalInterceptorProviderOptions>委托,并利用它添加了针对FoobarInterceptor与两个Func<Type, MethodInfo, bool>委托之间的关系,后者用来匹配目标方法(含属性方法)。
var foobar= new ServiceCollection() .AddSingleton<Foobar>() .BuildInterceptableServiceProvider(interception => interception.RegisterInterceptors(RegisterInterceptors)) .GetRequiredService<Foobar>(); foobar.M(); _ = foobar.P; foobar.P = null; Console.ReadLine(); static void RegisterInterceptors(ConditionalInterceptorProviderOptions options) { options.For<FoobarInterceptor>() .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<Type, MethodInfo, bool>委托与拦截器工厂的Func<IConventionalInterceptorFactory, Sortable<InvokeDelegate>>委托之间的映射关系,后者利用指定的IConventionalInterceptorFactory来创建一个对应的Sortable<InvokeDelegate>对象。
public class ConditionalInterceptorProvider : InterceptorProviderBase { private readonly ConditionalInterceptorProviderOptions _options; public ConditionalInterceptorProvider(IConventionalInterceptorFactory interceptorFactory, IOptions<ConditionalInterceptorProviderOptions> optionsAccessor) : 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<Sortable<InvokeDelegate>> GetInterceptors(Type targetType, MethodInfo method) => _options.Registrations.Where(it => it.Condition(targetType, method)).Select(it => it.Factory(InterceptorFactory)).ToList(); } public class ConditionalInterceptorProviderOptions { public IList<ConditionalInterceptorRegistration> Registrations { get; } = new List<ConditionalInterceptorRegistration>(); public Registry<TInterceptor> For<TInterceptor>(params object[] arguments)=> new(factory => factory.CreateInterceptor(typeof(TInterceptor), arguments), this); } public class Registry<TInterceptor> { private readonly Func<IConventionalInterceptorFactory, InvokeDelegate> _factory; private readonly ConditionalInterceptorProviderOptions _options; public Registry(Func<IConventionalInterceptorFactory, InvokeDelegate> factory, ConditionalInterceptorProviderOptions options) { _factory = factory; _options = options; } public Registry<TInterceptor> To(int order, Func<Type, MethodInfo, bool> condition) { var entry = new ConditionalInterceptorRegistration(condition, factory=>new Sortable<InvokeDelegate>(order, _factory(factory))); _options.Registrations.Add(entry); return this; } } public class ConditionalInterceptorRegistration { public Func<Type, MethodInfo, bool> Condition { get; } public Func<IConventionalInterceptorFactory, Sortable<InvokeDelegate>> Factory { get; } public ConditionalInterceptorRegistration(Func<Type, MethodInfo, bool> condition, Func<IConventionalInterceptorFactory, Sortable<InvokeDelegate>> factory) { Condition = condition; Factory = factory; } }
这一组映射关系利用ConditionalInterceptorProviderOptions的For<TInterceptor>方法进行添加,该方法返回一个Registry<TInterceptor>对象,后者提供的To方法指定了作为匹配条件的Func<Type, MethodInfo, bool>委托和决定拦截器执行顺序的Order值。ConditionalInterceptorProvider利用构造函数注入的IOptions<ConditionalInterceptorProviderOptions>得到这组映射关系,CanIntercept方法利用这组关系的匹配条件确定指定的方法是否应该被拦截,另一个GetInterceptors方法则利用匹配的工厂来创建返回的这组Sortable<InvokeDelegate>对象。
全新升级的AOP框架Dora.Interception[1]: 编程体验
全新升级的AOP框架Dora.Interception[2]: 基于约定的拦截器定义方式
全新升级的AOP框架Dora.Interception[3]: 基于“特性标注”的拦截器注册方式
全新升级的AOP框架Dora.Interception[4]: 基于“Lambda表达式”的拦截器注册方式
全新升级的AOP框架Dora.Interception[5]: 实现任意的拦截器注册方式
全新升级的AOP框架Dora.Interception[6]: 框架设计和实现原理
Recommend
-
25
Dora.Interception最初的定位就是专门针对.NET Core的AOP框架,所以在整个迭代过程中我大部分是在做减法。对于.NET Core程序开发来说,依赖注入已经成为无处不在并且“深入骨髓”的东西,不论是在进行业务应用的开发,还是进行基础组件的开...
-
45
The practice of HTTPS interception continues to be commonplace on the Internet. HT...
-
47
和所有的AOP框架一样,我们必须将正常的方法调用进行拦截,才能将应用到当前方法上的所有拦截器纳入当前调用链。Dora.Interception采用 IL Eimit 的方式实现对方法调用的拦截,接下来我们就来聊聊大致的实现原理。 ...
-
34
对于所有的AOP框架来说,多个拦截器最终会应用到某个方法上。这些拦截器按照指定的顺序构成一个管道,管道的另一端就是针对目标方法的调用。从设计角度来将,拦截器和中间件本质是一样的,那么我们可以按照类似的模式来设计拦截器。
-
7
Dora.Interception(github地址,觉得不错不妨给一颗星)有别于其他AOP框架的最大的一个特点就是采用针对“约定”的拦截器定义方式。如果我们为拦截器定义了一个接口或者基类,那么拦截方法将失去...
-
6
全新升级的AOP框架Dora.Interception[1]: 编程体验 ...
-
9
在Dora.Interception(github地址,觉得不错不妨给一颗星)中按照约定方式定义的拦截器可以采用多种方式注册到目标方法上。本篇文章介绍最常用的基于“特性标注”的拦截器注册方式,下一篇会介绍另...
-
5
如果拦截器应用的目标类型是由自己定义的,Dora.Interception(github地址,觉得不错不妨给一颗星)可以在其类型或成员上标注InterceptorAttribute特性来应用对应的拦截器。如果对那个的程序集是...
-
8
本系列前面的五篇文章主要介绍Dora.Interception(github地址,觉得不错不妨给一颗星)的编程模式以及对它的扩展定制,现在我们来聊聊它的设计和实现原理。(拙著《ASP.NET Core 6框架揭秘》
-
9
多年之前利用IL Emit写了一个名为Dora.Interception(github地址,觉得不错不妨给一颗星)的AOP框架。前几天利用Roslyn的Source Generator对自己为公司写的一个GraphQL框架进行改造,性能得到显...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK