学习路线指引(点击解锁) | 知识定位 | 人群定位 |
---|---|---|
Python实战微信订餐小程序 | 进阶级 | 本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。 |
Python量化交易实战 | 入门级 | 手把手带你打造一个易扩展、更安全、效率更高的量化交易系统 |
6折优惠,首印送签名专属书签)、
目录
一、IInterceptorProvider
二、InterceptorProviderBase
三、实现一种“万能”的拦截器注册方式
四、ConditionalInterceptorProvider
拦截器最终需要应用到某个具体的目标方法上,所以拦截器的注册就是如何建立拦截器与目标方法之间的映射关系,Dora.Interception将这一功能体现在如下所示的IInterceptorProvider接口上。顾名思义,IInterceptorProvider旨在解决为某个类型的某个方法提供拦截器列表的问题,这一个功能体现在GetInterceptors方法上。如下面的代码片段所示,该方法返回一组Sortable对象,InvokeDelegate代表拦截器本身,Sortable对象在此基础上添加了必要排序元素。
public interface IInterceptorProvider
{
bool CanIntercept(Type targetType, MethodInfo method, out bool suppressed);
IEnumerable> GetInterceptors(Type targetType, MethodInfo method);
void Validate(Type targetType, Action methodValidator, Action propertyValidator) {}
}
public sealed class Sortable
{
public int Order { get; }
public T Value { get; set; }
public Sortable(int order, T value)
{
Order = order;
Value = value;
}
}
除了GetInterceptors方法,IInterceptorProvider接口还定义了额外两个方法,CanIntercept方法用来判断指定的方式是否需要被拦截,代码生成器会利用这个方法决定如果生成最终可供拦截的代理类。另一个Validate方法用来验证针对指定类型的拦截器注册方式是否合法,即拦截器是否应用到一些根本无法被拦截的方法或者属性上,具体的检验逻辑由方法提供的两个委托来完成。
我们自定义的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> 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委托,并利用它添加了针对FoobarInterceptor与两个Func委托之间的关系,后者用来匹配目标方法(含属性方法)。
var foobar= new ServiceCollection()
.AddSingleton()
.BuildInterceptableServiceProvider(interception => interception.RegisterInterceptors(RegisterInterceptors))
.GetRequiredService();
foobar.M();
\_ = foobar.P;
foobar.P = null;
Console.ReadLine();
static void RegisterInterceptors(ConditionalInterceptorProviderOptions options)
{
options.For()
.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方法并未被拦截。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mrLyc1VY-1656305485829)(https://img2022.cnblogs.com/blog/19327/202206/19327-20220620142538346-2070714038.png “image”)]
上述这种针对匹配条件的“万能”注册方式是通过如下这个ConditionalInterceptorProvider类型实现的。ConditionalInterceptorProviderOptions类型定义了对应的配置选项,其核心就是一组ConditionalInterceptorRegistration对象的集合,而每一个ConditionalInterceptorRegistration对象是一个表示匹配条件的Func委托与拦截器工厂的Func>委托之间的映射关系,后者利用指定的IConventionalInterceptorFactory来创建一个对应的Sortable对象。
public class ConditionalInterceptorProvider : InterceptorProviderBase
{
private readonly ConditionalInterceptorProviderOptions \_options;
public ConditionalInterceptorProvider(IConventionalInterceptorFactory interceptorFactory, IOptions 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> GetInterceptors(Type targetType, MethodInfo method)
=> \_options.Registrations.Where(it => it.Condition(targetType, method)).Select(it => it.Factory(InterceptorFactory)).ToList();
}
public class ConditionalInterceptorProviderOptions
{
public IList Registrations { get; } = new List();
public Registry For(params object[] arguments)=> new(factory => factory.CreateInterceptor(typeof(TInterceptor), arguments), this);
}
public class Registry
{
private readonly Func \_factory;
private readonly ConditionalInterceptorProviderOptions \_options;
public Registry(Func factory, ConditionalInterceptorProviderOptions options)
{
\_factory = factory;
\_options = options;
}
public Registry To(int order, Funcbool> condition)
{
var entry = new ConditionalInterceptorRegistration(condition, factory=>new Sortable(order, \_factory(factory)));
\_options.Registrations.Add(entry);
return this;
}
}
public class ConditionalInterceptorRegistration
{
public Funcbool> Condition { get; }
public Func> Factory { get; }
public ConditionalInterceptorRegistration(Funcbool> condition, Func> factory)
{
Condition = condition;
Factory = factory;
}
}
这一组映射关系利用ConditionalInterceptorProviderOptions的For方法进行添加,该方法返回一个Registry对象,后者提供的To方法指定了作为匹配条件的Func委托和决定拦截器执行顺序的Order值。ConditionalInterceptorProvider利用构造函数注入的IOptions得到这组映射关系,CanIntercept方法利用这组关系的匹配条件确定指定的方法是否应该被拦截,另一个GetInterceptors方法则利用匹配的工厂来创建返回的这组Sortable对象。
全新升级的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]: 框架设计和实现原理