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

在Dora.Interception中按照约定方式定义的拦截器可以采用多种方式注册到目标方法上。本篇文章介绍最常用的基于“特性标注”的拦截器注册方式,下一篇会介绍另一种基于(Lambda)表达式的注册方式:全新升级的AOP框架Dora.Interception[4]: 基于表达式的拦截器注册。

目录
一、InterceptorAttribute 特性
二、指定构造拦截器的参数列表
三、将拦截器类型定义成特性
四、合法性检验
五、针对类型、属性的标注
六、拦截的屏蔽

一、InterceptorAttribute 特性

拦截器类型可以利用如下这个InterceptorAttribute特性应用到标注的类型、属性和方法上。除了通过Interceptor属性指定拦截器类型之外,我们还可以利用Order属性控制拦截器的执行顺序,该属性默认值为0。该特性的Arguments用来提供构造拦截器对象的参数。

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = true, Inherited = false)]
public class InterceptorAttribute : Attribute
{public Type Interceptor { get; }public object[] Arguments { get; }public int Order { get; set; }public InterceptorAttribute(params object[] arguments) :public InterceptorAttribute(Type? interceptor, params object[] arguments);
}

二、指定构造拦截器的参数列表

拦截器对象是通过依赖注入容器提供的,容器能够自动提供注入到构造函数中对象。如果构造函数包含额外的参数,对应的参数值就需要利用InterceptorAttribute 特性的Arguments属性来提供,此属性由构造函数的arguments参数提供。

public class FoobarInterceptor
{public string Name { get;  }public FoobarInterceptor(string name, IFoobar foobar){Name = name;Debug.Assert(foobar is not null);}public ValueTask InvokeAsync(InvocationContext invocationContext){Console.WriteLine($"FoobarInterceptor '{Name}' is invoked.");return invocationContext.ProceedAsync();}
}public interface IFoobar { }
public class Foobar : IFoobar { }

对于如上这个拦截器类型FoobarInterceptor,其构造函数定义了一个字符串的参数name用来指定拦截器的名称,当我利用InterceptorAttribute 特性将此拦截器应用到Invoker类型的Invoke1和Invoke2方法上是,就需要按照如下的方式指定具体的名称(Interceptor1和Interceptor2)。

public class Invoker
{[FoobarInterceptor("Interceptor1")]public virtual void Invoke1() => Console.WriteLine("Invoker.Invoke1()");[FoobarInterceptor("Interceptor2")]public virtual void Invoke2() => Console.WriteLine("Invoker.Invoke2()");
}

我们按照如下的方式调用Invoker对象的Invoke1和Invoke2方法。

var invoker = new ServiceCollection().AddSingleton<Invoker>().AddSingleton<IFoobar, Foobar>().BuildInterceptableServiceProvider().GetRequiredService<Invoker>();invoker.Invoke1();
invoker.Invoke2();

程序执行后,拦截器会以如下的形式将自身的名称输出到控制台上(源代码)。

ab261cb20808281b74565b8807b2b29b.png

三、将拦截器类型定义成特性

其实我们可以让定义的拦截器类型派生于InterceptorAttribute 特性,这样就可以直接将它标注到目标类型、属性和方法上。比如上面这个FoobarInterceptor类型可以改写成如下的形式。

public class FoobarInterceptorAttribute: InterceptorAttribute
{public string Name { get;  }public FoobarInterceptorAttribute(string name) => Name = name;public ValueTask InvokeAsync(InvocationContext invocationContext){Console.WriteLine($"FoobarInterceptor '{Name}' is invoked.");return invocationContext.ProceedAsync();}
}

那么它就可以按照如下的方式标注到Invoker类型的两个方法上(源代码)。

public class Invoker
{[FoobarInterceptor("Interceptor1")]public virtual void Invoke1() => Console.WriteLine("Invoker.Invoke1()");[FoobarInterceptor("Interceptor2")]public virtual void Invoke2() => Console.WriteLine("Invoker.Invoke2()");
}

四、合法性检验

只有接口方法和虚方法才能被拦截,Dora.Interception针对拦截器的应用提供了如下的验证逻辑:

  • 标注到方法上(函数属性的Get/Set方法):如果目标方法均不能被拦截,抛出异常;

  • 标注到属性上:表示将拦截器应用到该属性可以被拦截的Get/Set方法上。如果Get和Set方法均不能被拦截,抛出异常;

  • 标注到类型上:表示将拦截器应用到目标类型可以来拦截的方法(含属性方法)上,如果类型的所有方法均不能被拦截,此时不会抛出异常。

public class Foo
{[FoobarInterceptor]public void M() { }
}public class Bar
{[FoobarInterceptor]public object? P { get; set; }
}[FoobarInterceptor]
public class Baz
{public void M() { }
}

对于上面定义的三个类型,Foo的M方法和Bar的P属性均是无法被拦截,Baz类型并没有可以被拦截的方法。我们采用如下的程序测试上述的检验逻辑。

GetService<Foo>();
GetService<Bar>();
GetService<Baz>();static void GetService<T>() where T:class
{try{Console.WriteLine($"{typeof(T).Name}:");_ = new ServiceCollection().AddSingleton<T>().BuildInterceptableServiceProvider().GetRequiredService<T>();Console.WriteLine("OK");}catch (Exception ex){Console.WriteLine(ex.Message);}
}

程序运行后会在控制台上输出如下的结果,可以看出只有将拦截器应用到不合法的方法和属性上才会抛出异常(源代码)。

e8ee1da80c22f2c4e2c15e93cd835601.png

五、针对类型、属性的标注

我们利用如下这个拦截器类型FoobarInterceptorAttribute 来演示将拦截器应用到类型和属性上。该拦截器类型派生于InterceptorAttribute特性,并在执行的时候输出当前的方法。

public class FoobarInterceptorAttribute : InterceptorAttribute
{public ValueTask InvokeAsync(InvocationContext invocationContext){var method = invocationContext.MethodInfo;Console.WriteLine($"{method.DeclaringType!.Name}.{method.Name} is intercepted.");return invocationContext.ProceedAsync();}
}

我们将FoobarInterceptorAttribute 特性标注到Foo类型上,后者定义的M1方法和P1属性是可以被拦截的,但是M2方法和P2属性则不能。FoobarInterceptorAttribute 特性还被应用到Bar类型的P1属性以及P2属性的Set方法上。

[FoobarInterceptor]
public class Foo
{public virtual void M1() { }public void M2() { }public virtual object? P1 { get; set; }public object? P2 { get;   set; }
}public class Bar
{[FoobarInterceptor]public virtual object? P1 { get; set; }public virtual object? P2 { get; [FoobarInterceptor] set; }
}

我们利用如下的程序来检验针对Foo和Bar对象所有方法和属性的调用,那么被拦截器拦截下来。

var provider = new ServiceCollection().AddSingleton<Foo>().AddSingleton<Bar>().BuildInterceptableServiceProvider();var foo = provider.GetRequiredService<Foo>();
var bar = provider.GetRequiredService<Bar>();foo.M1();
foo.M2();
foo.P1 = null;
_ = foo.P1;
foo.P2 = null;
_ = foo.P2;
Console.WriteLine();bar.P1 = null;
_ = bar.P1;
bar.P2 = null;
_ = bar.P2;

程序运行之后会在控制台上输出如下的结果(源代码)。

cec6419766c2d675005b27318d2c8587.png

六、拦截的屏蔽

如果某个拦截器需要被应用大某个类型的绝大部分成员,我们可以选择“排除法”:将拦截器应用到该类型上,将某些非目标成员屏蔽掉。还有一种情况下,如果我们确定某些类型或者方法不能被拦截(比如会在一个循环中频繁调用),又担心一些“模糊”的拦截器注册方法将它们与某些拦截器错误地关联在一起,此时我们可以选择将其拦截功能显式屏蔽掉。

针对拦截的屏蔽可以通过在类型、属性、方法设置程序集上标注NonInterceptableAttribute特性。由于屏蔽功能具有最高优先级,一旦将此特性应用到某个类型上,该类型上的所有成员均不会被拦截。如果被标注到属性上,其Get和Set方法也不会被拦截。具有如下定义的Foo和Bar类型的所有方法和属性都不会被拦截(源代码)。

[FoobarInterceptor]
public class Foo
{[NonInterceptable]public virtual void M() { }[NonInterceptable]public virtual object? P1 { get; set; }public virtual object? P2 { [NonInterceptable] get; set; }
}[NonInterceptable]
public class Bar
{[FoobarInterceptor]public virtual void M() { }[FoobarInterceptor]public virtual object? P { get; set; }
}

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

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

相关文章

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

基于特性标注的拦截器注册方式仅限于将拦截器应用到自己定义的类型上&#xff0c;对于第三方提供的类型就无能为力了。对于Dora.Interception来说&#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…

fpga mysql_FPGA的一些琐碎知识整理

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

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;灵活运用还有好多变种。自从学…

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…

SCAU 算法课的题

8594 有重复元素的排列问题&#xff08;优先做&#xff09; 时间限制:1000MS 内存限制:1000K提交次数:1610 通过次数:656 题型: 编程题 语言: G;GCC;VC Description 设集合R{r1,r2,...,rn}是要进行排列的n个元素&#xff0c;其中r1,r2,...,rn可能相同。 试着设计一个算法&am…

react 数组新增_React 新特性 Hooks 讲解及实例(二)

本文是 React 新特性系列的第二篇&#xff0c;第一篇请点击这里&#xff1a;React 新特性讲解及实例什么是 HooksHook 是 React 16.8 的新增特性。它可以让你在不编写 类组件 的情况下使用 state以及其他的 React 特性。类组件的不足状态逻辑复用难缺少复用机制渲染属性和高阶组…

7天学会python_7天学会Python最佳可视化工具Seaborn(五):结构化展示多维数据

当探索具有中等数量(不多不少的意思……)维度的数据集时&#xff0c;一个很好的方式是基于不同的子数据集构建不同的实例&#xff0c;并将它们以网格的方式组织在一张图之中。这种技术有时被称为“lattice”或“trellis”(大概是格子图、网格图)&#xff0c;这跟“small multip…

面对峰值响应冲击,解决高并发的三大策略

2019独角兽企业重金招聘Python工程师标准>>> 当前在互联网的大潮下&#xff0c;众所周知淘宝、京东这些交易系统每天产生的数据量都是海量的&#xff0c;每天的交易并发也是惊人的&#xff0c;尤其是“双11”、“6.18”这些活动&#xff0c;对系统的峰值响应提出了非…

hibernate mysql 主从_MYSQL主从复制和写分离

基础篇https://edu.51cto.com/course/19845.htmlhttps://edu.51cto.com/course/19845.htmlhttps://edu.51cto.com/course/19841.htmlhttps://edu.51cto.com/course/21197.htmlhttps://edu.51cto.com/course/19886.htmlhttps://edu.51cto.com/course/19887.htmlhttps://edu.51ct…

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

本系列前面的五篇文章主要介绍Dora.Interception的编程模式以及对它的扩展定制&#xff0c;现在我们来聊聊它的设计和实现原理。目录一、调用链抽象二、基于约定的拦截器定义三、基于调用上下文的依赖注入容器四、拦截器的提供五、调用链的构建六、方法拦截的实现原理七、依赖注…

完成登录与注册页面的前端

完成登录与注册页面的HTMLCSSJS&#xff0c;其中的输入项检查包括&#xff1a; 用户名6-12位 首字母不能是数字 只能包含字母和数字 密码6-12位 注册页两次密码是否一致 JS&#xff1a; function fnLogin() {var uSer document.getElementById("user");var pAss do…

WPF效果第二百零一篇之实现合并单元格

早一段时间又一次出差青海省西宁市;回来又是总结又是各种琐事,也没顾得上去分享点东西;大周末的就在家分享一下,这二天再次基于ListBox实现的合并单元格的效果:1、ListBox嵌套ListBox的前台布局:<ListBox ItemsSource"{Binding LCPListData}" x:Name"Manufac…

ASP.NET Core中使用EasyCaching作为缓存抽象层

简介做后端开发&#xff0c;缓存应该是天天在用&#xff0c;很多时候我们的做法是写个帮助类&#xff0c;然后用到的时候调用一下。这种只适合简单层次的应用&#xff1b;一旦涉及到接口实现调整之类的&#xff0c;这种强耦合的做法很不合适。有些其他的功能又要去重复造轮子。…