可以传入 ABP vNext 定义的 IAbpInterceptor
作为其泛型参数。
public class CastleAbpInterceptorAdapter<TInterceptor> : IInterceptor
where TInterceptor : IAbpInterceptor
{
}
Castle 的拦截器也会有一个 Intercept()
方法,该方法将在被拦截方法执行的时候触发。在触发之后,会根据当前方法的定义进行不同的操作,这里异步方法和同步方法处理逻辑是不一样的。
public void Intercept(IInvocation invocation)
{
var proceedInfo = invocation.CaptureProceedInfo();
var method = invocation.MethodInvocationTarget ?? invocation.Method;
// 判断执行的方法是否是异步方法。
if (method.IsAsync())
{
InterceptAsyncMethod(invocation, proceedInfo);
}
else
{
InterceptSyncMethod(invocation, proceedInfo);
}
}
这里我们以异步方法为例,其内部又会根据方法的返回值是否是 Task 进行不同的操作,因为如果是泛型的 Task,说明该异步方法是有返回值的,所以处理逻辑也不一样。
private void InterceptAsyncMethod(IInvocation invocation, IInvocationProceedInfo proceedInfo)
{
if (invocation.Method.ReturnType == typeof(Task))
{
invocation.ReturnValue = MethodExecuteWithoutReturnValueAsync
.Invoke(this, new object[] { invocation, proceedInfo });
}
else
{
invocation.ReturnValue = MethodExecuteWithReturnValueAsync
.MakeGenericMethod(invocation.Method.ReturnType.GenericTypeArguments[0])
.Invoke(this, new object[] {invocation, proceedInfo});
}
}
进一步解析在返回类型为 Task
时,它所调用的方法。
private async Task ExecuteWithoutReturnValueAsync(IInvocation invocation, IInvocationProceedInfo proceedInfo)
{
// 注意这里,该用法在之前的 C# 多线程学习笔记文章有说过,作用是出让当前核心给其他线程。
await Task.Yield();
// 调用真实的拦截器,根据传入的方法调用模型去拦截真实的方法。
await _abpInterceptor.InterceptAsync(
new CastleAbpMethodInvocationAdapter(invocation, proceedInfo)
);
}
从上述代码可以得知,ABP vNext 的拦截器动作现在被包裹在一个 Castle 拦截器内部进行的。
那么,我们的 Castle.Core 拦截器在什么时候与类型进行绑定的呢,每个拦截器又是如何与特性的类型进行注册的呢?这里我以审计日志拦截器为例,看一下它在系统当中是如何注册,并被使用的。
审计日志相关的代码存放在 Volo.Abp.Auditing 库中,我们找到 AuditingInterceptor
类型,查看其定义可以看到它也是继承自 AbpInterceptor
抽象基类。
public class AuditingInterceptor : AbpInterceptor, ITransientDependency
{
}
接着我们根据名字找到了拦截器的注册工具类 AuditingInterceptorRegistrar
,在类型的定义当中 ShouldIntercept()
与 ShouldAuditTypeByDefault()
根据传入的 Type 类型,根据特定的逻辑决定是否为该类型关联审计日志拦截器。
private static bool ShouldIntercept(Type type)
{
if (ShouldAuditTypeByDefault(type))
{
return true;
}
// 如果类型的任意方法启用了 Auditied 特性,则应用拦截器。
if (type.GetMethods().Any(m => m.IsDefined(typeof(AuditedAttribute), true)))
{
return true;
}
return false;
}
public static bool ShouldAuditTypeByDefault(Type type)
{
// 判断类型是否使用了 Audited 特性,使用了则应用审计日志拦截器。
if (type.IsDefined(typeof(AuditedAttribute), true))
{
return true;
}
// 判断类型是否使用了 DisableAuditing 特性,使用了则不关联拦截器。
if (type.IsDefined(typeof(DisableAuditingAttribute), true))
{
return false;
}
// 如果类型实现了 IAuditingEnabled 接口,则启用拦截器。
if (typeof(IAuditingEnabled).IsAssignableFrom(type))
{
return true;
}
return false;
}
我们这里需要关注的是 RegisterIfNeeded()
方法,它在审计日志模块的预加载方法就被添加到了一个 ServiceRegistrationActionList
集合当中,这个集合会在后面 AutoFac 进行类型注册的时候被使用。
public static void RegisterIfNeeded(IOnServiceRegistredContext context)
{
// 如果类型允许被审计日志拦截器所拦截,则在类型关联的拦截器上下文当中添加审计日志拦截器。
if (ShouldIntercept(context.ImplementationType))
{
context.Interceptors.TryAdd<AuditingInterceptor>();
}
}
public override void PreConfigureServices(ServiceConfigurationContext context)
{
// 将这个 Action 加入 List。
context.Services.OnRegistred(AuditingInterceptorRegistrar.RegisterIfNeeded);
}