ABP 攔截器
ABP 大量採用 AOP 設計將橫切關注點抽離到攔截器中,保持程式碼乾淨且易於維護。 底層利用 Castle Core 整合 Autofac 實現 AOP,但 ABP 為了避免直接依賴第三方函式庫,額外封裝了一層抽象,以便未來擴充。
Castle Core 與整合 Autofac 的基礎知識可以參考之前的文章:
核心概念
以下是幾個關鍵的知識點:
- 函式庫依賴
Castle.Core:提供 DynamicProxy,實現攔截功能。Castle.Core.AsyncInterceptor:簡化非同步攔截器實作。Autofac.Extras.DynamicProxy:將 Castle DynamicProxy 整合進 Autofac DI。Autofac.Extensions.DependencyInjection:使用 Autofac 替代原生 DI。
- 攔截器流程
Castle.Core攔截器呼叫invocation.Proceed()回到原流程並執行原始方法。Castle.Core.AsyncInterceptor建立的非同步攔截器透過AsyncDeterminationInterceptor將IAsyncInterceptor包裝成IInterceptor。AsyncDeterminationInterceptor內部會自動判斷攔截的方法是Synchronous、AsyncAction、AsyncFunction三個其中一種,並呼叫對應攔截程式。Autofac.Extras.DynamicProxy只看得懂IInterceptor介面。EnableInterfaceInterceptors()、EnableClassInterceptors()分別用於介面與類別的攔截。InterceptedBy需要指定獨立類別名稱以區分不同攔截器。
- 非同步攔截器實作方式(底層都是實作
IAsyncInterceptor)- 實作
IAsyncInterceptor - 繼承
AsyncInterceptorBase - 繼承
ProcessingAsyncInterceptor<TState>
- 實作
建立 ABP 攔截器
首先參考一下內建的 UnitOfWorkInterceptor 攔截器,它用來將當前請求包裝成一個事務:
- 繼承
AbpInterceptor並註冊為 Transient。 - 建立
RegisterIfNeeded委派,透過ShouldIntercept()方法,判斷是否將此攔截器加入OnServiceRegistredContext.Interceptors清單。 - 所有攔截器的
RegisterIfNeeded委派集中到ServiceRegistrationActionList,程式啟動時透過UseAutofac()方法依序執行所有委派完成註冊。
UnitOfWorkInterceptor 核心程式碼
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DynamicProxy;
namespace Volo.Abp.Uow;
public class UnitOfWorkInterceptor : AbpInterceptor, ITransientDependency
{
private readonly IServiceScopeFactory _serviceScopeFactory;
public UnitOfWorkInterceptor(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = serviceScopeFactory;
}
public override async Task InterceptAsync(IAbpMethodInvocation invocation)
{
if (!UnitOfWorkHelper.IsUnitOfWorkMethod(invocation.Method, out var unitOfWorkAttribute))
{
await invocation.ProceedAsync();
return;
}
using (var scope = _serviceScopeFactory.CreateScope())
{
var options = CreateOptions(scope.ServiceProvider, invocation, unitOfWorkAttribute);
var unitOfWorkManager = scope.ServiceProvider.GetRequiredService<IUnitOfWorkManager>();
//Trying to begin a reserved UOW by AbpUnitOfWorkMiddleware
if (unitOfWorkManager.TryBeginReserved(UnitOfWork.UnitOfWorkReservationName, options))
{
await invocation.ProceedAsync();
if (unitOfWorkManager.Current != null)
{
await unitOfWorkManager.Current.SaveChangesAsync();
}
return;
}
using (var uow = unitOfWorkManager.Begin(options))
{
await invocation.ProceedAsync();
await uow.CompleteAsync();
}
}
}
private AbpUnitOfWorkOptions CreateOptions(IServiceProvider serviceProvider, IAbpMethodInvocation invocation, UnitOfWorkAttribute? unitOfWorkAttribute)
{
var options = new AbpUnitOfWorkOptions();
unitOfWorkAttribute?.SetOptions(options);
if (unitOfWorkAttribute?.IsTransactional == null)
{
var defaultOptions = serviceProvider.GetRequiredService<IOptions<AbpUnitOfWorkDefaultOptions>>().Value;
options.IsTransactional = defaultOptions.CalculateIsTransactional(
autoValue: serviceProvider.GetRequiredService<IUnitOfWorkTransactionBehaviourProvider>().IsTransactional
?? !invocation.Method.Name.StartsWith("Get", StringComparison.InvariantCultureIgnoreCase)
);
}
return options;
}
}
攔截器註冊
using System;
using System.Reflection;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DynamicProxy;
namespace Volo.Abp.Uow;
public static class UnitOfWorkInterceptorRegistrar
{
public static void RegisterIfNeeded(IOnServiceRegistredContext context)
{
if (ShouldIntercept(context.ImplementationType))
{
context.Interceptors.TryAdd<UnitOfWorkInterceptor>(); // 加入到 `OnServiceRegistredContext` 的 `Interceptors` 清單中
}
}
private static bool ShouldIntercept(Type type)
{
return !DynamicProxyIgnoreTypes.Contains(type) && UnitOfWorkHelper.IsUnitOfWorkType(type.GetTypeInfo());
}
}
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity;
namespace Volo.Abp.Uow;
public class AbpUnitOfWorkModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.OnRegistered(UnitOfWorkInterceptorRegistrar.RegisterIfNeeded);
}
}
註冊流程分析
在 UseAutofac() 執行的過程中,透過 AbpRegistrationBuilderExtensions 的 InvokeRegistrationActions 與 AddInterceptors 擴充方法將攔截器註冊到容器:
- 若服務是介面,呼叫
EnableInterfaceInterceptors()。 - 若服務是類別,呼叫
EnableClassInterceptors()。 InterceptedBy註冊的攔截器為泛型類別AbpAsyncDeterminationInterceptor<TInterceptor>, 因此最終註冊的形式為AbpAsyncDeterminationInterceptor<UnitOfWorkInterceptor>, 負責將IAsyncInterceptor轉接成IInterceptor。
private static IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> InvokeRegistrationActions<TLimit, TActivatorData, TRegistrationStyle>(
this IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> registrationBuilder,
ServiceRegistrationActionList registrationActionList,
Type serviceType,
Type implementationType,
object? serviceKey = null)
where TActivatorData : ReflectionActivatorData
{
var serviceRegistredArgs = new OnServiceRegistredContext(serviceType, implementationType, serviceKey);
foreach (var registrationAction in registrationActionList)
{
registrationAction.Invoke(serviceRegistredArgs);
}
if (serviceRegistredArgs.Interceptors.Any())
{
var disableAbpFeaturesAttribute = serviceRegistredArgs.ImplementationType.GetCustomAttribute<DisableAbpFeaturesAttribute>(true);
if (disableAbpFeaturesAttribute == null || !disableAbpFeaturesAttribute.DisableInterceptors)
{
registrationBuilder = registrationBuilder.AddInterceptors(
registrationActionList,
serviceType,
serviceRegistredArgs.Interceptors
);
}
}
return registrationBuilder;
}
private static IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle>
AddInterceptors<TLimit, TActivatorData, TRegistrationStyle>(
this IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> registrationBuilder,
ServiceRegistrationActionList serviceRegistrationActionList,
Type serviceType,
IEnumerable<Type> interceptors)
where TActivatorData : ReflectionActivatorData
{
if (serviceType.IsInterface)
{
registrationBuilder = registrationBuilder.EnableInterfaceInterceptors();
}
else
{
if (serviceRegistrationActionList.IsClassInterceptorsDisabled ||
serviceRegistrationActionList.DisabledClassInterceptorsSelectors.Any(selector => selector.Predicate(serviceType)))
{
return registrationBuilder;
}
(registrationBuilder as IRegistrationBuilder<TLimit, ConcreteReflectionActivatorData, TRegistrationStyle>)?.EnableClassInterceptors();
}
foreach (var interceptor in interceptors)
{
registrationBuilder.InterceptedBy(
typeof(AbpAsyncDeterminationInterceptor<>).MakeGenericType(interceptor)
);
}
return registrationBuilder;
}
轉接層
AbpAsyncDeterminationInterceptor 是 ABP 建立的抽象封裝,繼承的類別是之前提到過的 AsyncDeterminationInterceptor 關鍵類別,
能夠轉接成 IInterceptor 攔截器。
這裡將 CastleAsyncAbpInterceptorAdapter<TInterceptor> 將 ABP 攔截器轉接給 Castle:
using Castle.DynamicProxy;
using Volo.Abp.DynamicProxy;
namespace Volo.Abp.Castle.DynamicProxy;
public class AbpAsyncDeterminationInterceptor<TInterceptor> : AsyncDeterminationInterceptor
where TInterceptor : IAbpInterceptor
{
public AbpAsyncDeterminationInterceptor(TInterceptor abpInterceptor)
: base(new CastleAsyncAbpInterceptorAdapter<TInterceptor>(abpInterceptor))
{
}
}
CastleAsyncAbpInterceptorAdapter 則是繼承 AsyncInterceptorBase 類別,我們已經知道它的底層就是 IAsyncInterceptor 介面,
這個轉接層就是 Castle 攔截器呼叫 ABP Castle 攔截器的地方,也就是開始執行 UnitOfWorkInterceptor 的 InterceptAsync 方法。
using System;
using System.Threading.Tasks;
using Castle.DynamicProxy;
using Volo.Abp.DynamicProxy;
namespace Volo.Abp.Castle.DynamicProxy;
public class CastleAsyncAbpInterceptorAdapter<TInterceptor> : AsyncInterceptorBase
where TInterceptor : IAbpInterceptor
{
private readonly TInterceptor _abpInterceptor;
public CastleAsyncAbpInterceptorAdapter(TInterceptor abpInterceptor)
{
_abpInterceptor = abpInterceptor;
}
protected override async Task InterceptAsync(IInvocation invocation, IInvocationProceedInfo proceedInfo, Func<IInvocation, IInvocationProceedInfo, Task> proceed)
{
await _abpInterceptor.InterceptAsync(
new CastleAbpMethodInvocationAdapter(invocation, proceedInfo, proceed)
);
}
protected override async Task<TResult> InterceptAsync<TResult>(IInvocation invocation, IInvocationProceedInfo proceedInfo, Func<IInvocation, IInvocationProceedInfo, Task<TResult>> proceed)
{
var adapter = new CastleAbpMethodInvocationAdapterWithReturnValue<TResult>(invocation, proceedInfo, proceed);
await _abpInterceptor.InterceptAsync(
adapter
);
return (TResult)adapter.ReturnValue;
}
}
另外 ABP 為了避免直接依賴 Castle Core 的 invocation.Proceed() 方法呼叫原始流程,所以引入了 IAbpMethodInvocation 介面
透過 CastleAbpMethodInvocationAdapter 類別將原本 Castle Core 的資訊包裝成 ABP 專用的 IAbpMethodInvocation 介面
因此 UnitOfWorkInterceptor 只需要呼叫 invocation.ProceedAsync() 背後就會透過轉接層呼叫 Castle Core 的 invocation.Proceed()。
using System;
using System.Threading.Tasks;
using Castle.DynamicProxy;
using Volo.Abp.DynamicProxy;
namespace Volo.Abp.Castle.DynamicProxy;
public class CastleAbpMethodInvocationAdapter : CastleAbpMethodInvocationAdapterBase, IAbpMethodInvocation
{
protected IInvocationProceedInfo ProceedInfo { get; }
protected Func<IInvocation, IInvocationProceedInfo, Task> Proceed { get; }
public CastleAbpMethodInvocationAdapter(IInvocation invocation, IInvocationProceedInfo proceedInfo,
Func<IInvocation, IInvocationProceedInfo, Task> proceed)
: base(invocation)
{
ProceedInfo = proceedInfo;
Proceed = proceed;
}
public override async Task ProceedAsync()
{
await Proceed(Invocation, ProceedInfo);
}
}
Summary
ABP 的 AOP 流程大致如下:
- 當呼叫
MyService.DoWorkAsync()時,由於應用程式啟動階段已透過 Autofac 將所有攔截器預先綁定到服務上, 此時實際被呼叫的物件並非 MyService 本身,而是 Castle DynamicProxy 產生的 Proxy 物件。 - Proxy 會依序執行已註冊的攔截器,其中包含
AbpAsyncDeterminationInterceptor<UnitOfWorkInterceptor>。 - 由於
AbpAsyncDeterminationInterceptor<UnitOfWorkInterceptor>繼承自AsyncDeterminationInterceptor, 它會在內部判斷目前呼叫的MyService.DoWorkAsync()是非同步方法,並自動轉交給CastleAsyncAbpInterceptorAdapter的InterceptAsync方法處理。 CastleAsyncAbpInterceptorAdapter透過依賴注入取得UnitOfWorkInterceptor, 將 Castle 的IInvocation、ProceedInfo資訊封裝成 ABP 專用的CastleAbpMethodInvocationAdapter, 再呼叫UnitOfWorkInterceptor.InterceptAsync(CastleAbpMethodInvocationAdapter)。UnitOfWorkInterceptor.InterceptAsync先執行前置邏輯(開始事務), 接著呼叫invocation.ProceedAsync(),流程便會回到 Proxy 物件,執行真正的MyService.DoWorkAsync()方法。- 當原始方法執行完成後,流程會再返回
UnitOfWorkInterceptor.InterceptAsync,執行後置邏輯(提交或回滾交易), 最後將結果一路回傳給的呼叫端。
Client
|
v
呼叫 MyService.DoWorkAsync()
|
v
IMyService 介面與攔截器在程式啟動時已經綁定好
|
v
AbpAsyncDeterminationInterceptor<UnitOfWorkInterceptor>
|
v
CastleAsyncAbpInterceptorAdapter<UnitOfWorkInterceptor>
|
v
UnitOfWorkInterceptor.InterceptAsync
┌──────────────────────────┐
│ 前置邏輯(開始事務) │
│ invocation.ProceedAsync()│ -> MyService.DoWorkAsync()
│ 後置邏輯(提交/回滾) │
└──────────────────────────┘
|
v
結果回傳給 Client