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

基于特性标注的拦截器注册方式仅限于将拦截器应用到自己定义的类型上,对于第三方提供的类型就无能为力了。对于Dora.Interception来说,拦截器注册本质上建立拦截器与一个或者多个目标方法之间的映射,所以最笨的方式就是利用反射的方式得到表示目标方法的MethodInfo对象,并将它与对应的拦截器关联在一起。这种方式虽然能够解决问题,但是编程体验很差。本篇介绍的基于表达式的拦截器注册方式利用针对目标方法或者属性的调用表达式,以一种强类型的方式获取到目标方法,极大地改善了编程体验。

一、IInterceptorRegistry

以表达式采用强类型的方式将指定类型的拦截器应用到目标方法上是借助如下这个IInterceptorRegistry接口完成的。IInterceptorRegistry接口提供了一个For<TInterceptor>方法以待注册的拦截器类型关联,参数arguments用来提供构建拦截器对象的参数。该方法会返回一个IInterceptorRegistry<TInterceptor>对象,它提供了一系列的方法帮助我们将指定的拦截器应用到指定目标类型(通过泛型参数类型TTarget表示)相应的方法上。

public interface IInterceptorRegistry
{IInterceptorRegistry<TInterceptor> For<TInterceptor>(params object[] arguments);...
}public interface IInterceptorRegistry<TInterceptor>
{IInterceptorRegistry<TInterceptor> ToAllMethods<TTarget>(int order);IInterceptorRegistry<TInterceptor> ToMethod<TTarget>(int order, Expression<Action<TTarget>> methodCall);IInterceptorRegistry<TInterceptor> ToMethod(int order, Type targetType, MethodInfo method);IInterceptorRegistry<TInterceptor> ToGetMethod<TTarget>(int order, Expression<Func<TTarget, object?>> propertyAccessor);IInterceptorRegistry<TInterceptor> ToSetMethod<TTarget>(int order, Expression<Func<TTarget, object?>> propertyAccessor);IInterceptorRegistry<TInterceptor> ToProperty<TTarget>(int order, Expression<Func<TTarget, object?>> propertyAccessor);
}

封装了IServiceCollection集合的InterceptionBuilder提供了一个RegisterInterceptors扩展方法,我们可以利用该方法定义的Action<IInterceptorRegistry>类型的参数来使用上述的这个IInterceptorRegistry接口。不论是IServiceCollection接口的BuildInterceptableServiceProvider扩展方法,还是IHostBuilder接口的UseInterception方法均提供了一个可选的Action<InterceptionBuilder>委托类型的参数。

public sealed class InterceptionBuilder
{public IServiceCollection Services { get; }public InterceptionBuilder(IServiceCollection services);  
}public static class Extensions
{public static InterceptionBuilder RegisterInterceptors(this InterceptionBuilder builder, Action<IInterceptorRegistry> register);public static IServiceProvider BuildInterceptableServiceProvider(this IServiceCollection services, Action<InterceptionBuilder>? setup = null);public static IHostBuilder UseInterception(this IHostBuilder hostBuilder, Action<InterceptionBuilder>? setup = null);
}

二、将拦截器应用到某个类型

类似与将InterceptorAttribute标注到某个类型上,我们也可以采用这种方式将指定的拦截器应用到目标类型上,背后的含义就是应用到该类型可以被拦截的所以方法上(含属性方法)。

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

我们可以采用如下的方式将调用IInterceptorRegistry<TInterceptor>的ToAllMethods<TTarget>方法将上面定义的拦截器FoobarInterceptor应用到Foobar类型的所有方法上。

var foobar = new ServiceCollection().AddSingleton<Foobar>().BuildInterceptableServiceProvider(interception => interception.RegisterInterceptors(RegisterInterceptors)).GetRequiredService<Foobar>();foobar.M();
foobar.P = null;
_ = foobar.P;static void RegisterInterceptors(IInterceptorRegistry registry)
{var foobar = registry.For<FoobarInterceptor>();foobar.ToAllMethods<Foobar>(order: 1);
}

从如下所示的执行结果可以看出,Foobar类型的M方法和P属性均被FoobarInterceptor拦截下来(源代码)。

741f6118b6ad6a71614c9a999dd3c87a.png

三、应用到指定的方法和属性

我们可以通过指定调用方法或者获取属性的表达式来指定拦截器应用的目标方法。我们将目标类型Foobar定义成如下的形式,两个重载的M方法和三个属性均是可以拦截的。

public class Foobar
{public virtual void M(int x, int y) { }public virtual void M(double x, double y) { }public virtual object? P1 { get; set; }public virtual object? P2 { get; set; }public virtual object? P3 { get; set; }
}

我们利用如下的代码将上面定义的FoobarInterceptor应用到Foobar类型相应的成员上。具体来说,我们调用ToMethod<TTarget>方法应用到两个重载的M方法,调用ToProperty<TTarget>方法应用到P1属性的Get和Set方法上,调用ToGetMethod<TTarget>和ToSetMethod<TTarget>方法应用到P2属性的Get方法和P3属性的Set方法。

var provider = new ServiceCollection().AddSingleton<Foobar>().BuildInterceptableServiceProvider(interception => interception.RegisterInterceptors(RegisterInterceptors));var foobar = provider.GetRequiredService<Foobar>();foobar.M(1, 1);
foobar.M(3.14, 3.14);
foobar.P1 = null;
_ = foobar.P1;
foobar.P2 = null;
_ = foobar.P2;
foobar.P3 = null;
_ = foobar.P3;
Console.ReadLine();static void RegisterInterceptors(IInterceptorRegistry registry)
{var foobar = registry.For<FoobarInterceptor>();foobar.ToMethod<Foobar>(order: 1, it => it.M(default(int), default(int))).ToMethod<Foobar>(order: 1, it => it.M(default(double), default(double))).ToProperty<Foobar>(order: 1, it => it.P1).ToGetMethod<Foobar>(order: 1, it => it.P2).ToSetMethod<Foobar>(order: 1, it => it.P3);
}

程序运行后,针对Foobar相应成员的拦截体现在如下所示的输出结果上(源代码)。

6111d37bfd769af4de347690e3b8cfde.png

四、指定构建拦截器的参数

如果应用的拦截器类型构造函数指定了参数,我们采用这种注册方式的时候也可以指定参数。以如下这个FoobarInterceptor为例,其构造函数中指定了两个参数,一个是代表拦截器名称的name参数,另一个是IFoobar对象。

public class FoobarInterceptor
{public FoobarInterceptor(string name, IFoobar foobar){Name = name;Foobar = foobar;}public string Name { get; }public IFoobar Foobar { get; }public ValueTask InvokeAsync(InvocationContext invocationContext){Console.WriteLine($"{invocationContext.MethodInfo.Name} is intercepted by FoobarInterceptor {Name}.");Console.WriteLine($"Foobar is '{Foobar.GetType()}'.");return invocationContext.ProceedAsync();}
}
public interface IFoobar { }
public class Foo : IFoobar { }
public class Bar: IFoobar { }public class Invoker
{public virtual void M1() { }public virtual void M2() { }
}

由于字符串参数name无法从依赖注入容器提取,所以在注册FoobarInterceptor是必须显式指定。如果容器能够提供IFoobar对象,但是希望指定一个不通过的对象,也可以在注册的时候显式指定一个IFoobar对象。我们按照如下的方式将两个不同的FoobarInterceptor对象分别应用到Invoker类型的Invoke1和Invoke2方法上,并分别将名称设置为Interceptor1和Interceptor2,第二个拦截器还指定了一个Bar对象作为参数(容器默认提供的IFoobar对象的类型为Foo)。

var invoker = new ServiceCollection().AddSingleton<Invoker>().AddSingleton<IFoobar, Foo>().BuildInterceptableServiceProvider(interception => interception.RegisterInterceptors(RegisterInterceptors)).GetRequiredService<Invoker>();invoker.M1();
Console.WriteLine();
invoker.M2();static void RegisterInterceptors(IInterceptorRegistry registry)
{registry.For<FoobarInterceptor>("Interceptor1").ToMethod<Invoker>(order: 1, it => it.M1());registry.For<FoobarInterceptor>("Interceptor2", new Bar()).ToMethod<Invoker>(order: 1, it => it.M2());
}

程序运行之后,两个FoobarInterceptor对象的名称和依赖的IFoobar对象的类型以如下的形式输出到控制台上(源代码)。

f76f2e9d1974c53ff1b82efb5912163f.png

五、拦截屏蔽

除了用来注册指定拦截器的For<TInterceptor>方法,IInterceptorRegistry接口还定义了如下这些用来屏蔽拦截的SuppressXxx方法。

public interface IInterceptorRegistry
{IInterceptorRegistry<TInterceptor> For<TInterceptor>(params object[] arguments);IInterceptorRegistry SupressType<TTarget>();IInterceptorRegistry SupressTypes(params Type[] types);IInterceptorRegistry SupressMethod<TTarget>(Expression<Action<TTarget>> methodCall);IInterceptorRegistry SupressMethods(params MethodInfo[] methods);IInterceptorRegistry SupressProperty<TTarget>(Expression<Func<TTarget, object?>> propertyAccessor);IInterceptorRegistry SupressSetMethod<TTarget>(Expression<Func<TTarget, object?>> propertyAccessor);IInterceptorRegistry SupressGetMethod<TTarget>(Expression<Func<TTarget, object?>> propertyAccessor);
}

我们可以采用如下的方式会将屏蔽掉Foobar类型所有成员的拦截特性,虽然拦截器FoobarInterceptor被注册到了这个类型上(源代码)。

var foobar = new ServiceCollection().AddSingleton<Foobar>().BuildInterceptableServiceProvider(interception => interception.RegisterInterceptors(RegisterInterceptors)).GetRequiredService<Foobar>();
...static void RegisterInterceptors(IInterceptorRegistry registry)
{registry.For<FoobarInterceptor>().ToAllMethods<Foobar>(order: 1);registry.SupressType<Foobar>();
}

下面的程序明确屏蔽掉Foobar类型如下这些方法的拦截能力:M方法,P1属性的Get和Set方法(如果有)以及P属性的Get方法(源代码)。

var foobar = new ServiceCollection().AddSingleton<Foobar>().BuildInterceptableServiceProvider(interception => interception.RegisterInterceptors(RegisterInterceptors)).GetRequiredService<Foobar>();...static void RegisterInterceptors(IInterceptorRegistry registry)
{registry.For<FoobarInterceptor>().ToAllMethods<Foobar>(order: 1);registry.SupressMethod<Foobar>(it=>it.M());registry.SupressProperty<Foobar>(it => it.P1);registry.SupressGetMethod<Foobar>(it => it.P2);
}

六、两个后备方法

通过指定调用目标方法或者提取属性的表达式来提供拦截器应用的方法和需要屏蔽的方法提供了较好的编程体验,但是能够提供这种强类型编程模式的前提是目标方法或者属性是公共成员。对于受保护(protected)的方法和属性,我们只能使用如下两个后备方法,指定代表目标方法的MethodInfo对象。

public interface IInterceptorRegistry<TInterceptor>
{IInterceptorRegistry<TInterceptor> ToMethods<TTarget>(int order, params MethodInfo[] methods);
}public interface IInterceptorRegistry
{IInterceptorRegistry SupressMethods(params MethodInfo[] methods);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/281976.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql8.0.12插件_MySQL8.0.12 安装及配置

MySQL8.0.12 安装及配置发布时间&#xff1a;2018-08-07 10:39,浏览次数&#xff1a;274, 标签&#xff1a;MySQL一.安装1.从网上下载MySQL8.0.12版本&#xff0c;下载地址&#xff1a;https://dev.mysql.com/downloads/mysql/2. 下载完成后解压我解压的路径是&#xff1a;D:\J…

python模块之hashlib

hashlib模块实现了多种安全哈希和信息摘要算法的通用接口&#xff0c;包括FIPS中定义的SHA1, SHA224, SHA256, SHA384, SHA512以及RFC 1321中定义的MD5 注意点&#xff1a;1. adler32及crc32哈希由zlib模块提供2. 某些算法已知存在哈希碰撞弱点 哈希算法 每个hash算法都有一个同…

记一次阿里电面经历

昨天下午&#xff08;3/19&#xff09;三点多钟&#xff0c;接到了一个杭州的电话&#xff0c;是阿里的。问我是否方便聊聊。我说我在上课&#xff0c;四点下课。然后他就四点多钟的时候又打了一次过来。项目经历上来就问我有无大型项目的经历。不好意思&#xff0c;我说无。。…

C语言程序设计第三次作业

&#xff08;一&#xff09;改错题 计算f(x)的值&#xff1a;输入实数x&#xff0c;计算并输出下列分段函数f(x)的值&#xff0c;输出时保留1位小数。 输入输出样例1&#xff1a;   Enterr x: 10.0   f(10.0) 0.1 输入输出样例2&#xff1a;   Enter x: 234   f(234.0…

mysql数据库项目化教程郑小蓉_MySQL数据库项目化教程(高等职业教育“十三五”规划教材(软件技术专业))...

《MySQL数据库项目化教程/高等职业教育十三五规划教材(软件技术专业)》是一本介绍MySQL数据库基础知识的入门教材&#xff0c;采用项目驱动方式循序渐进地介绍MySQL各个模块的知识。主要内容包括&#xff1a;Windows下MySQL的安装&#xff0c;MySQL服务的启动与停止&#xff0c…

WPF-09 ManualResetEventSlim信号量

业务场景如下&#xff1a;WPF应用程序后台有个定时任务在接收PLC硬件数据(该线程接收完数据之后, 会重新启动一个新线程继续接收.....)&#xff0c;当应用程序关闭时, 我们得确保接收PLC硬件数据的线程完成之后,再关闭应用程序&#xff0c;否则会造成数据丢失。上面的业务场景是…

【bzoj3033】太鼓达人 DFS欧拉图

题目描述 给出一个整数K&#xff0c;求一个最大的M&#xff0c;使得存在一个每个位置都是0或1的圈&#xff0c;圈上所有连续K位构成的二进制数两两不同。输出最大的M以及这种情况下字典序最小的方案。 输入 一个整数K。 输出 一个整数M和一个二进制串&#xff0c;由一个空格分隔…

Redis 集合处理

学习了列表之后&#xff0c;发现了Redis处理字符串的功能强大。 为了适应不同场景的需求&#xff0c;还有一个用的很多的就是集合。 Redis提供的集合支持的类型是字符串。并且集合中的元素值是唯一的&#xff0c;也就是说不能出现重复数据。 而且&#xff0c;集合的实现是通过哈…

fpga mysql_FPGA的一些琐碎知识整理

1.生产FPGA的厂家有&#xff1a;ALTERAXILINXATCELLatticeps:Altera和Xilinx主要生产一般用途FPGA&#xff0c;其主要产品采用SRAM工艺Actel主要提供非易失性FPGA&#xff0c;产品主要基于反熔丝工艺和FLASH工艺ps: 熔丝&#xff0c;顾名思义&#xff1a;把丝熔掉&#xff0c;反…

使用增量备份修复DG中的GAP

问题描述 oracle中DG出现主备不同步现象&#xff0c;alert日志报警有gap信息&#xff0c;但是v$archive_gap视图查不到任何信息。同时主库上的对应归档已经删除且没有备份 解决方案 1.查询备库的scn SQL> select current_scn from v$database; 这时有可能出来的scn是以科学计…

C# 反射类Assembly用法举例

概述程序运行时&#xff0c;通过反射可以得到其它程序集或者自己程序集代码的各种信息&#xff0c;包括类、函数、变量等来实例化它们&#xff0c;执行它们&#xff0c;操作它们&#xff0c;实际上就是获取程序在内存中的映像&#xff0c;然后基于这个映像进行各种操作。Assemb…

团队作业

团队&组员&#xff1a; 没有组名&#xff0c;大概是因为我们组虽然有10个人&#xff0c;但是好像只起到人多的地方就容易开车搞笑&#xff0c;没有内涵&#xff0c;取出来的都是秋名山吴彦组这样的开车组名&#xff0c;在大家的的强烈建议和玩笑中&#xff0c;决定了没有组…

算法系列【希尔排序】篇

常见的内部排序算法有&#xff1a;插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。用一张图概括&#xff1a;关于时间复杂度&#xff1a;1. 平方阶 (O(n2)) 排序各类简单排序&#xff1a;直接插入、直接选择和冒泡排序。2. 线性对数…

sql查询索引语句_sql优化总结--基于sql语句优化和索引优化

概述最近做查询&#xff0c;统计和汇总。由于数据量比较庞大&#xff0c;大部分表数据上百万&#xff0c;甚至有的表数据上千万。所以在系统中做sql优化比较多&#xff0c;特此写一篇文章总结一下关于sql优化方面的经验。导致查询缓慢的原因1、数据量过大2、表设计不合理3、sql…

电商行业运维实践

电商行业运维实践&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;…

数据结构小总结(成都磨子桥技工学校数据结构前12题)

[pixiv] https://www.pixiv.net/member_illust.php?modemedium&illust_id34352147 暑假的作业&#xff0c;颓颓的我总算是写完了 线段树 线段树是一个高级玩意&#xff0c;不仅可以求区间和&#xff0c;区间最大等等的简单问题&#xff0c;灵活运用还有好多变种。自从学…

【九章算法免费讲座第一期】转专业找CS工作的“打狗棒法”

讲座时间&#xff1a; 美西时间6月5日18&#xff1a;30-20&#xff1a;00&#xff08;周五&#xff09; 北京时间6月6日09&#xff1a;30-11&#xff1a;00&#xff08;周六a.m&#xff09; 讲座安排&#xff1a; 免费在线直播讲座 报名网址&#xff1a; http://t.cn/R2XgMSH&a…

golang mysql 防注入_Go,Gorm 和 Mysql 是如何防止 SQL 注入的

Go&#xff0c;Gorm 和 Mysql 是如何防止 SQL 注入的SQL 注入和 SQL 预编译技术什么是 SQL 注入所谓SQL注入(sql inject)&#xff0c;就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串&#xff0c;最终达到欺骗服务器执行恶意的SQL命令。具体来说&#xff…

wav2midi 音乐旋律提取算法 附可执行demo

前面提及过&#xff0c;音频指纹算法的思路。 也梳理开源了两个比较经典的算法。 https://github.com/cpuimage/shazam https://github.com/cpuimage/AudioFingerprinter 后来一段时间&#xff0c;稍微看了下这两个算法&#xff0c;还有不少可以精简优化的空间。 例如抗噪&…

全新升级的AOP框架Dora.Interception[5]: 实现任意的拦截器注册方式

Dora.Interception提供了两种拦截器注册方式&#xff0c;一种是利用标注在目标类型、属性和方法上的InterceptorAttribute特性&#xff0c;另一种采用基于目标方法或者属性的调用表达式。通过提供的扩展点&#xff0c;我们可以任何我们希望的拦截器注册方式。目录一、IIntercep…