Dora.Interception,为.NET Core度身打造的AOP框架 [3]:多样化拦截器应用方式
在《以约定的方式定义拦截器》中,我们通过对拦截器的介绍了Dora.Interception的两种拦截机制,即针对接口的“实例拦截”针对虚方法的“类型拦截”。我们介绍了拦截器的本质以及基于约定的拦截器定义方式,接下来我们将着重关注拦截器的应用问题。
一、拦截器应用解决什么问题
和有些AOP框架不同,Dora.Interception在设计刻意地将拦截器和拦截器应用刻意地分开。不仅如此,在拦截器和拦截器应用之间,我们还分离出“拦截器管道的构建”:
- 拦截器:旨在完成单一拦截功能的实现;
- 拦截器管道的构建:将多个拦截器按照指定的顺序构建一个管道;
- 拦截器的应用:将构建的拦截器管道应用到被拦截的某个方法上。
二、IInterceptorChainBuilder
拦截器管道的构建由IInterceptorChainBuilder来完成,它类似于ASP.NET Core的IApplicationBuilder接口,后者利用注册的中间件来构建一个中间件管道,而IInterceptorChainBuilder则采用类似的方式将注册的拦截器构建成一个拦截器管道。如下面的代码片段所示,我们利用Use方法将表示拦截器的InterceptorDelegate 的委托对象提供给IInterceptorChainBuilder,该方法的order参数表示提供的拦截器最终在拦截器链条上的位置。拦截器管道的构建最终由Build方法来完成,构建的管道也体现为一个InterceptorDelegate类型的委托。
- public interface IInterceptorChainBuilder
- {
- InterceptorDelegate Build();
- IInterceptorChainBuilder New();
- IInterceptorChainBuilder Use(InterceptorDelegate interceptor, int order);
- IServiceProvider ServiceProvider { get; }
- }
由于Dora.Interception是为.NET Core度身定制的,而.NET Core总是离不开通过通过IServiceProvider表示的DI容器,所以我们将IServiceProvider整合到IInterceptorChainBuilder中,我们在构建拦截器管道过程中所需的任何一个依赖服务都可以利用它来提取。IInterceptorChainBuilder的New方法用来创建一个新的IInterceptorChainBuilder对象,当我们开始构建一个管道的时候需要调用此方法。
虽然Dora.Interception最终总是利用InterceptorDelegate对象来表示拦截器,但是我们推荐应用程序采用我们约定的方式将拦截器定义成一个POCO类型,所谓我们为IInterceptorChainBuilder定义了如下几个扩展方法来注册一次方式定义的拦截器类型。如果调用第一个和第三个Use方法提供拦截器类型(第二个Use方法直接提供的是拦截器对象),我们最终需要利用作为DI容器的IServiceProvider对象来创建对应的实例。如果构造函数中所有的参数都是预选注册的服务,我们无需提供任何的参数,否则就需要利用arguments来提供它们。
- public static class InterceptorChainBuilderExtensions
- {
- public static IInterceptorChainBuilder Use<TInterceptor>(this IInterceptorChainBuilder builder, int order, params object[] arguments);
- public static IInterceptorChainBuilder Use(this IInterceptorChainBuilder builder, object interceptor, int order);
- public static IInterceptorChainBuilder Use(this IInterceptorChainBuilder builder, Type interceptorType, int order, params object[] arguments);
- }
三、IInterceptorProvider
IInterceptorChainBuilder仅仅是一个用来构建拦截器管道的工具而已,最终向它提供原材料(拦截器)的是一个IInterceptorProvider对象。一般来说一个拦截器类型对应一个IInterceptorProvider实现(也可可以是多个)。如下面的代码片段所示,IInterceptorProvider同样定义了一个Use方法,该方法将上面这个IInterceptorChainBuilder作为参数。在具体的实现中,我们一般会调用IInterceptorChainBuilder的Use方法来注册对应的拦截器类型。IInterceptorProvider还具有一个AllowMultiple属性表示当前类型的多个拦截器实例能够同时存在于管道中。
- public interface IInterceptorProvider
- {
- void Use(IInterceptorChainBuilder builder);
- bool AllowMultiple { get; }
- }
IInterceptorProvider接口实际上体现了拦截器的注册方法,因为所谓的拦截器注册本质上体现为如何向IInterceptorChainBuilder提供拦截器的问题。由于标准Attribute是我们推荐的注册方式,我们为它们定义了如下这个名为InterceptorAttribute的基类。InterceptorAttribute可以标注到类型、属性和方法上,它的AllowMultiple属性与标注到该Attribute上的AttributeUsageAttribute的AllowMultiple属性一致,默认值为False。
- [AttributeUsage((AttributeTargets) (AttributeTargets.Property | AttributeTargets.Method | AttributeTargets.Class), AllowMultiple=false)]
- public abstract class InterceptorAttribute : Attribute, IInterceptorProvider,
- {
- public abstract void Use(IInterceptorChainBuilder builder);
- public bool AllowMultiple { get; }
- public int Order { get; set; }
- }
比如在前面一章中,我们定义了如下这么一个典型的Interceptor类型:
- public class FoobarInterceptor
- {
- public IFoo Foo { get; }
- public string Baz { get; }
- public FoobarInterceptor(IFoo foo, string baz)
- {
- Foo = foo;
- Baz = baz;
- }
- public async Task InvokeAsync(InvocationContext context, IBar bar)
- {
- await Foo.DoSomethingAsync();
- await bar.DoSomethingAsync();
- await context.ProceedAsync();
- }
- }
我们可以为它定义如下这么一个类型为FoobarInterceptorAttribute 的IInterceptorProvider的实现。
- [AttributeUsage(AttributeTargets.Class| AttributeTargets.Method)]
- public class FoobarInterceptorAttribute : InterceptorAttribute
- {
- public string Baz { get; }
- public FoobarInterceptorAttribute(string baz) => Baz = baz;
- public override void Use(IInterceptorChainBuilder builder) => builder.Use<FoobarInterceptor>(Order, Baz);
- }
虽然Dora.Interception是将Interceptor和IInterceptorProvider区分开来,但是应用程序可以采用如下的方式将它们合二为一。其实将它们分而治之还有一个好处,那就是我可以为IInterceptorProvider起一个不同的名称,比如第一篇演示的实例中我们将拦截器命名为CachingInterceptor,但是对应的IInterceptorProvider实现类型则定义成CacheReturnValueAttribute。还有另一个好处就是可以为同一个拦截器名类型定义多一个不同的IInterceptorProvider实现。
- public class FoobarInterceptorAttribute : InterceptorAttribute
- {
- public string Baz { get; }
- public FoobarInterceptorAttribute(string baz) => Baz = baz;
- public async Task InvokeAsync(InvocationContext context, IFoo foo, IBar bar)
- {
- await foo.DoSomethingAsync();
- await bar.DoSomethingAsync();
- await context.ProceedAsync();
- }
- public override void Use(IInterceptorChainBuilder builder) => builder.Use(this, Order);
- }
四、IInterceptorProviderResolver
最终针对拦截器的应用体现在IInterceptorProviderResolver对象上。IInterceptorProvider其实帮助我们解决了一个核心问题:提供具体的拦截器并将它存放到对应的位置(即在最终构建的拦截器管道中的Order)。那么针对拦截器的应用最终体现为:针对一个类型或者其成员(方法或者属性),能够提供怎样的IInterceptorProvider。如下面的代码片段所示,IInterceptorProviderResolver提供了三个方法来解析应用到类型、方法和属性(Get、Set或者Both)的IInterceptorProvider。
- public interface IInterceptorProviderResolver
- {
- bool? WillIntercept(Type targetType);
- IInterceptorProvider[] GetInterceptorProvidersForMethod(Type targetType, MethodInfo targetMethod);
- IInterceptorProvider[] GetInterceptorProvidersForProperty(Type targetType, PropertyInfo targetProperty, PropertyMethod getOrSet);
- IInterceptorProvider[] GetInterceptorProvidersForType(Type targetType);
- }
- [Flags]
- public enum PropertyMethod
- {
- Get = ,
- Set = ,
- Both = ,
- }
我们说标注Attribute仅仅体现为针对拦截器的一种注册方式而已,因为在Dora.Interception我们为它定义了如下这么一个AttributeInterceptorProviderResolver,它是默认注册的。
- internal class AttributeInterceptorProviderResolver : IInterceptorProviderResolver
- {
- public IInterceptorProvider[] GetInterceptorProvidersForMethod(Type targetType, MethodInfo method);
- public IInterceptorProvider[] GetInterceptorProvidersForProperty(Type targetType, PropertyInfo property, PropertyMethod propertyMethod);
- public IInterceptorProvider[] GetInterceptorProvidersForType(Type type);
- public bool? WillIntercept(Type type);
- public bool? WillIntercept(Type targetType, MethodInfo method);
- public bool? WillIntercept(Type targetType, PropertyInfo property);
- }
五、自行实现你需要的拦截器应用方式
如果我们觉得基于Attribute的实现不能满足你的需求,只需要自行实现上面这个IInterceptorProviderResolver接口就可以了。比如我们可以定义如下这个“万能”的IInterceptorProviderResolver实现,因为我将针对IInterceptorProvider对象与目标方法的匹配规则定义成一个Func<MethodInfo, bool>。
- public class InterceptorRegistry : IInterceptorProviderResolver
- {
- private readonly IInterceptorProvider[] _empty = new IInterceptorProvider[];
- private readonly Dictionary<IInterceptorProvider, Func<MethodInfo, bool>> _policies = new Dictionary<IInterceptorProvider, Func<MethodInfo, bool>>();
- public IInterceptorProvider[] GetInterceptorProvidersForMethod(Type targetType, MethodInfo targetMethod)
- => _policies.Where(it => it.Value(targetMethod)).Select(it => it.Key).ToArray();
- public IInterceptorProvider[] GetInterceptorProvidersForProperty(Type targetType, PropertyInfo targetProperty, PropertyMethod getOrSet)
- {
- switch (getOrSet)
- {
- case PropertyMethod.Get:
- return GetInterceptorProvidersForMethod(targetType, targetProperty.GetMethod);
- case PropertyMethod.Set:
- return GetInterceptorProvidersForMethod(targetType, targetProperty.SetMethod);
- default:
- return GetInterceptorProvidersForMethod(targetType, targetProperty.GetMethod)
- .Union(GetInterceptorProvidersForMethod(targetType, targetProperty.SetMethod))
- .ToArray();
- }
- }
- public IInterceptorProvider[] GetInterceptorProvidersForType(Type targetType) => _empty;
- public bool? WillIntercept(Type targetType)
- {
- if (targetType.GetCustomAttributes<NonInterceptableAttribute>().Any())
- {
- return false;
- }
- return null;
- }
- public InterceptorRegistry Add(IInterceptorProvider interceptorProvider, Func<MethodInfo, bool> filter)
- {
- _policies.Add(interceptorProvider, filter);
- return this;
- }
- }
这样的自定义IInterceptorProviderResolver(InterceptorRegistry )可以采用如下的方式进行注册。
- public class Startup
- {
- public void ConfigureServices(IServiceCollection services)
- {
- var registry = new InterceptorRegistry()
- .Add(new CacheReturnValueAttribute(), method => method.Name == "GetCurrentTime" && method.DeclaringType == typeof(SystemClock));
- services.AddInterception(builder=>builder.InterceptorProviderResolvers.Add("policy",registry));
- }
- ...
- }
或者
- public class Startup
- {
- public IServiceProvider ConfigureServices(IServiceCollection services)
- {
- var registry = new InterceptorRegistry()
- .Add(new CacheReturnValueAttribute(), method => method.Name == "GetCurrentTime" && method.DeclaringType == typeof(SystemClock));
- return services.BuildInterceptableServiceProvider(builder=>builder.InterceptorProviderResolvers.Add("policy",registry));
- }
- }
通过自定义IInterceptorProviderResolver可以帮助我们实现任意形式的拦截器注册方式,但是千万不能滥用。我个人的观点是:这种用于注册拦截器的规则必需是明确的,我们必需非常确切地知道拦截器最终应用到了哪个方法上。如果定义的规则太过模糊,比如针对方法名称进行注册,那么我们的拦截器极有可能应用到某个我们并不希望的方法上。
[1]:更加简练的编程体验
[2]:基于约定的拦截器定义方式
[3]:多样性的拦截器应用方式
[4]:与依赖注入框架的深度整合
[5]:对拦截机制的灵活定制
Dora.Interception,为.NET Core度身打造的AOP框架 [3]:多样化拦截器应用方式的更多相关文章
- Dora.Interception,为.NET Core度身打造的AOP框架:全新的版本
Dora.Interception 1.0(Github地址:可以访问GitHub地址:https://github.com/jiangjinnan/Dora)推出有一段时间了,最近花了点时间将它升级 ...
- Dora.Interception, 为.NET Core度身打造的AOP框架[4]:演示几个典型应用
为了帮助大家更深刻地认识Dora.Interception,并更好地将它应用到你的项目中,我们通过如下几个简单的实例来演示几个常见的AOP应用在Dora.Interception下的实现.对于下面演示 ...
- Dora.Interception, 为.NET Core度身打造的AOP框架:不一样的Interceptor定义方式
相较于社区其他主流的AOP框架,Dora.Interception在Interceptor提供了完全不同的编程方式.我们并没有为Interceptor定义一个接口,正是因为不需要实现一个预定义的接口, ...
- Dora.Interception, 为.NET Core度身打造的AOP框架[3]:Interceptor的注册
在<不一样的Interceptor>中我们着重介绍了Dora.Interception中最为核心的对象Interceptor,以及定义Interceptor类型的一些约定.由于Interc ...
- Dora.Interception,为.NET Core度身打造的AOP框架 [2]:以约定的方式定义拦截器
上一篇<更加简练的编程体验>提供了最新版本的Dora.Interception代码的AOP编程体验,接下来我们会这AOP框架的编程模式进行详细介绍,本篇文章着重关注的是拦截器的定义.采用“ ...
- Dora.Interception, 一个为.NET Core度身打造的AOP框架:不一样的Interceptor定义方式
相较于社区其他主流的AOP框架,Dora.Interception在Interceptor提供了完全不同的编程方式.我们并没有为Interceptor定义一个接口,正是因为不需要实现一个预定义的接口, ...
- Dora.Interception, 一个为.NET Core度身打造的AOP框架[3]:Interceptor的注册
在<不一样的Interceptor>中我们着重介绍了Dora.Interception中最为核心的对象Interceptor,以及定义Interceptor类型的一些约定.由于Interc ...
- Dora.Interception,为.NET Core度身打造的AOP框架 [1]:更加简练的编程体验
很久之前开发了一个名为Dora.Interception的开源AOP框架(github地址:https://github.com/jiangjinnan/Dora,如果你觉得这个这框架还有那么一点价值 ...
- Dora.Interception,为.NET Core度身打造的AOP框架 [5]:轻松地实现与其他AOP框架的整合
这里所谓的与第三方AOP框架的整合不是说改变Dora.Interception现有的编程,而是恰好相反,即在不改变现有编程模式下采用第三方AOP框架或者自行实现的拦截机制.虽然我们默认提供基于IL E ...
随机推荐
- 移动端右侧导航 显示隐藏js
transform与fixed影响 html按钮 <span class="nav-btn"></span> <span class="cl ...
- 小米平板4 Plus获取Root超级权限的步骤
小米平板4 Plus有么好方法开启Root权限?大家都清楚,Android机器有Root权限,一旦手机开启root相关权限,就可以实现更强大的功能,打比方大家部门的营销部门的同事,使用个别营销应用都需 ...
- centos7系统部署cobbler批量安装系统
系统环境: 一.开启两个网卡.一个仅主机模式,一个桥接模式,主机模式对内提供cobbler服务 [root@localhost ~]# ip a 1: lo: <LOOPBACK,UP,LOWE ...
- 使用npm命令,而不用cnpm命令,也可以得到同样的体验
以前大家都知道使用cnpm命令来替代npm,可以大大提升下载各种包的速度.例如: npm install -g cnpm --registry=https://registry.npm.taobao. ...
- Java序列化随记
序列化简介: 程序中的对象并不只是存在内存中,还需要传输网络,或者保存起来下次再加载出来用,因此需要Java序列化技术. Java序列化技术正是将对象转变成一串由二进制字节组成的数组,可以通过将二进制 ...
- Gradle 同步时报错,Could not find com.android.support.constraint:constraint-layout:1.0.0-alpha8的解决方法
Error:Could not find com.android.support.constraint:constraint-layout:1.0.0-alpha8. 原因: SDK 中可能是没有安装 ...
- SSL 3.0曝出Poodle漏洞的解决方案
tomcat 各版本对ssl解决方案的配置:tomcat6: <Connector port="443" protocol="org.apache.coyote.h ...
- 前端如何做好seo
一:什么是SEO? 搜索引擎优化(Search Engine Optimization),简称SEO.是按照搜索引擎给出的优化建议,以增强网站核心价值为目标,从网站结构.内容建设方案.用户互动传播等角 ...
- log4j、使用log4j、打印sql日志
添加pom文件依赖 <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifa ...
- Linux NFS服务器的安装与配置方法(图文详解)
这篇文章主要介绍了Linux NFS服务器的安装与配置方法(图文详解),需要的朋友可以参考下(http://xb.xcjl0834.com) 一.NFS服务简介 NFS 是Network File S ...