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]: 框架设计和实现原理

全新升级的AOP框架Dora.Interception[6]: 实现任意的拦截器注册方式的更多相关文章

  1. 全新升级的AOP框架Dora.Interception[1]: 编程体验

    多年之前利用IL Emit写了一个名为Dora.Interception(github地址,觉得不错不妨给一颗星)的AOP框架.前几天利用Roslyn的Source Generator对自己为公司写的 ...

  2. 全新升级的AOP框架Dora.Interception[2]: 基于&ldquo;约定&rdquo;的拦截器定义方式

    Dora.Interception有别于其他AOP框架的最大的一个特点就是采用针对"约定"的拦截器定义方式.如果我们为拦截器定义了一个接口或者基类,那么拦截方法将失去任意注册依赖服 ...

  3. 全新升级的AOP框架Dora.Interception[3]: 基于特性标注的拦截器注册方式

    在Dora.Interception(github地址,觉得不错不妨给一颗星)中按照约定方式定义的拦截器可以采用多种方式注册到目标方法上.本篇文章介绍最常用的基于"特性标注"的拦截 ...

  4. 全新升级的AOP框架Dora.Interception[4]: 基于Lambda表达式的拦截器注册方式

    如果拦截器应用的目标类型是由自己定义的,Dora.Interception(github地址,觉得不错不妨给一颗星)可以在其类型或成员上标注InterceptorAttribute特性来应用对应的拦截 ...

  5. 全新升级的AOP框架Dora.Interception[6]: 框架设计和实现原理

    本系列前面的五篇文章主要介绍Dora.Interception(github地址,觉得不错不妨给一颗星)的编程模式以及对它的扩展定制,现在我们来聊聊它的设计和实现原理.(拙著<ASP.NET C ...

  6. 全新升级的AOP框架Dora.Interception[汇总,共6篇]

    多年之前利用IL Emit写了一个名为Dora.Interception(github地址,觉得不错不妨给一颗星)的AOP框架.前几天利用Roslyn的Source Generator对自己为公司写的 ...

  7. AOP框架Dora.Interception 3.0 [1]: 编程体验

    .NET Core正式发布之后,我为.NET Core度身定制的AOP框架Dora.Interception也升级到3.0.这个版本除了升级底层类库(.NET Standard 2.1)之外,我还对它 ...

  8. AOP框架Dora.Interception 3.0 [2]: 实现原理

    和所有的AOP框架一样,我们必须将正常的方法调用进行拦截,才能将应用到当前方法上的所有拦截器纳入当前调用链.Dora.Interception采用IL Eimit的方式实现对方法调用的拦截,接下来我们 ...

  9. AOP框架Dora.Interception 3.0 [5]: 基于策略的拦截器注册方式

    注册拦截器旨在解决如何将拦截器应用到目标方法的问题.在我看来,针对拦截器的注册应该是明确而精准的,也就是我们提供的注册方式应该让拦截器准确地应用到期望的目标方法上,不能多也不能少.如果注册的方式过于模 ...

随机推荐

  1. html显示与隐藏元素的几种方式

    html显示与隐藏元素的几种方式 1.display none : 无 隐藏元素 block : 显示 转换为块级元素   不占位:当隐藏的时候元素就完全没有了.不能看见和操作该元素. 优点:为其他元 ...

  2. JavaScript函数中的arguments对象

    ECMAScript标准中,每个函数都有一个特殊的内置对象arguments.arguments对象是一个类Array对象(object),用以保存函数接收到的实参副本. 一.内置特性 说它是一个内置 ...

  3. zookeeper篇-zoo.cfg配置

    点赞再看,养成习惯,微信搜索「小大白日志」关注这个搬砖人. 文章不定期同步公众号,还有各种一线大厂面试原题.我的学习系列笔记. zoo.cfg即/usr/local/java/zookeeper/co ...

  4. 攻防世界-MISC:stegano

    这是攻防世界新手练习区的第五题,题目如下: 点击附件1下载,得到一个pdf文件,打开后内容如下: 把pdf文件里的内容复制到记事本上,发现一串A和B的字符串,不知道是什么(真让人头大) 参考一下WP, ...

  5. Maven基础学习笔记

    Maven基础学习笔记 下载链接 官网:https://maven.apache.org/ 所有版本:https://archive.apache.org/dist/maven/maven-3/ 阿里 ...

  6. 一篇讲清楚String、StringBuffer和StringBuild

    ​ ​ 一.String篇 1.String基本介绍? (jdk文档原文)String类代表字符串. Java程序中的所有字符串文字(例如"abc" )都被实现为此类的实例. 说人 ...

  7. (Bezier)贝塞尔曲在路径规划的运用

    前言 之前被安排了活,一个局部区域机器运动控制的工作,大致是一个机器位于一个极限区域时候,机器要进入一个特殊的机制,使得机器可以安全的走出来.其中用到了bezier曲线进行优化路径,今天写一下,正好也 ...

  8. 什么叫做 SSO

    什么叫做 SSO 本文写于 2020 年 12 月 8 日 SSO 的全称叫做 Single Sign On,意味「单点登录」. 何为单点登录?就是你希望自己的两个网站,可以做到:一个网站登录了,另一 ...

  9. docker+nginx+redis部署前后端分离项目!!!

    介绍本文用的经典的前后端分离开源项目.项目的拉取这些在另一篇博客!!! 其中所需要的前后端打包本篇就不做操作了!!不明白的去看另一篇博客!!! 地址:http://www.cnblogs.com/ps ...

  10. leetcode 142. Linked List Cycle II 环形链表 II

    一.题目大意 https://leetcode.cn/problems/linked-list-cycle-ii/ 给定一个链表的头节点  head ,返回链表开始入环的第一个节点. 如果链表无环,则 ...