前言
書接上回,上回我們了解了 castle 代理的一些缺點(diǎn),本文將開始操作整合 Microsoft.Extension.Dependency和Castle,以讓默認(rèn)的容器可以支持?jǐn)r截器
我們將以進(jìn)階的形式逐步完善我們的封裝,以實(shí)現(xiàn)一個(gè)更方便易用、普適、高性能的基礎(chǔ)設(shè)施庫(kù)。
基礎(chǔ)版
還是先上代碼, 這是基礎(chǔ)版本我們要達(dá)成的目標(biāo),僅需定義一個(gè)特性即可完成攔截的目標(biāo)
/// <summary>
///
/// </summary>
public abstract class InterceptorBaseAttribute : Attribute, IInterceptor
{
void IInterceptor.Intercept(IInvocation invocation)
{
var returnType = invocation.Method.ReturnType;
var builder = AsyncMethodBuilder.TryCreate(returnType);
if (builder != null)
{
var asyncInvocation = new AsyncInvocation(invocation);
var stateMachine = new AsyncStateMachine(asyncInvocation, builder, task: InterceptAsync(asyncInvocation));
builder.Start(stateMachine);
invocation.ReturnValue = builder.Task();
}
else
{
Intercept(invocation);
}
}
protected virtual void Intercept(IInvocation invocation) { }
protected abstract ValueTask InterceptAsync(IAsyncInvocation invocation);
......
}
如上是我們定義的攔截器基類,我們想要達(dá)到的目標(biāo)是,只要繼承該基類,并覆寫InterceptAsync 方法即可實(shí)現(xiàn)具有特定功能的攔截類,而容器會(huì)自動(dòng)代理到該攔截類,實(shí)現(xiàn)攔截。
這里要感謝 https://github.com/stakx/DynamicProxy.AsyncInterceptor 的作者,該庫(kù)采用 MIT 的許可使用協(xié)議,我們可以直接參考使用。
接下來,是重頭戲,考慮到易用性,我們以 Microsoft.Extension.DependencyInjection 為基本庫(kù),實(shí)現(xiàn)一個(gè)擴(kuò)展類,用于實(shí)現(xiàn)攔截器功能。
代碼如下:
public static class CastleServiceCollectionExtensions
{
public static IServiceCollection ConfigureCastleDynamicProxy(this IServiceCollection services)
{
services.TryAddSingleton<ProxyGenerator>(sp => new ProxyGenerator());
//TODO:1.從IServiceCollection中獲取 方法定義InterceptorBaseAttribute特性子類的ServiceDescriptor
//TODO:2.逐個(gè)處理,獲取每個(gè)ServiceDescriptor中的ServiceType,識(shí)別是具體類還是接口,然后獲取InterceptorBaseAttribute特性子類的實(shí)例
//作為攔截器,借用proxyGenerator 去創(chuàng)建對(duì)應(yīng)的代理然后添加到IServiceCollection中
//TODO:3 移除原始對(duì)應(yīng)的ServiceType注冊(cè)
return services;
}
}
在注釋中我們簡(jiǎn)單描述了該擴(kuò)展方法的實(shí)現(xiàn)過程,我們采用移花接木的方式替換掉原有ServiceType的注冊(cè),將代理對(duì)象注冊(cè)為ServiceType的實(shí)現(xiàn)即可。
第一步我們這么實(shí)現(xiàn)
var descriptors = services.Where(svc =>svc.ServiceType.GetMethods()
.Any(i => i.GetCustomAttributes(false).Any(i => i.GetType().IsAssignableTo(typeof(InterceptorBaseAttribute))))).ToList();
第二步的核心是 ServiceDescriptor 中 三種生成場(chǎng)景的分開處理,至于是哪三種場(chǎng)景可以看下我的第一篇文章 https://www.cnblogs.com/gainorloss/p/17961153
- descriptor.ImplementationType 有值:已知ServiceType和ImplementationType
偽代碼如下
implementationFactory = sp =>
{
var generator = sp.GetRequiredService<ProxyGenerator>();
var interceptors = GetInterceptors(descriptor.ServiceType);//獲取攔截器 galoS@2024-1-12 14:47:47
var proxy = descriptor.ServiceType.IsClass
? generator.CreateClassProxy(descriptor.ServiceType, interceptors.ToArray())
: generator.CreateInterfaceProxyWithoutTarget(descriptor.ServiceType, interceptors.ToArray());
return proxy;
};
- descriptor.ImplementationInstance 有值:已知ServiceType和 實(shí)現(xiàn)對(duì)象實(shí)例
implementationFactory = sp =>
{
var generator = sp.GetRequiredService<ProxyGenerator>();
var interceptors = GetInterceptors(descriptor.ServiceType, sp);//獲取攔截器 galoS@2024-1-12 14:47:47
var proxy = descriptor.ServiceType.IsClass
? generator.CreateClassProxyWithTarget(descriptor.ServiceType, descriptor.ImplementationInstance, interceptors.ToArray())
: generator.CreateInterfaceProxyWithTarget(descriptor.ServiceType, descriptor.ImplementationInstance, interceptors.ToArray());
return proxy;
};
- descriptor.ImplementationFactory 有值:已知ServiceType和 實(shí)現(xiàn)工廠方法
implementationFactory = sp =>
{
var generator = sp.GetRequiredService<ProxyGenerator>();
var interceptors = GetInterceptors(descriptor.ServiceType, sp);//獲取攔截器 galoS@2024-1-12 14:47:47
var proxy = descriptor.ServiceType.IsClass
? generator.CreateClassProxyWithTarget(descriptor.ServiceType, descriptor.ImplementationInstance, interceptors.ToArray())
: generator.CreateInterfaceProxyWithTarget(descriptor.ServiceType, descriptor.ImplementationInstance, interceptors.ToArray());
return proxy;
};
可以看到 2,3比較雷同,因?yàn)槟玫?實(shí)例和通過委托傳入IServiceProvider拿到實(shí)例,其實(shí)結(jié)果是相似的,最終我們都使用工廠注入的形式 生成新的 ServiceDescriptor services.AddTransinet(descriptor.ServiceType, implementationFactory);
。
最后一步 移除即可
偽代碼如下services.Remove(descriptor);
改造一下之前的代碼并測(cè)試
var services = new ServiceCollection();
services.AddLogging();//此處添加日志服務(wù) 偽代碼 以便獲取ILogger<SampleService>
services.TryAddTransient<SampleService>();
services.TryAddTransient<ISampleService, SampleService>();
services.ConfigureCastleDynamicProxy();//一定要在最后,不然會(huì)有些服務(wù)無法代理到 2024-1-13 13:53:05
var sp = services.BuildServiceProvider();
var proxy = sp.GetRequiredService<SampleService>();
var name = await proxy.ShowAsync();
/// <summary>
/// 異常捕獲、日志記錄和耗時(shí)監(jiān)控 攔截器 2024-1-12 21:28:22
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class CatchLoggingInterceptor : InterceptorBaseAttribute
{
protected override async ValueTask InterceptAsync(IAsyncInvocation invocation)
{
//TODO:類注釋所寫的邏輯
await Console.Out.WriteLineAsync("Interceptor starting...");
Console.WriteLine("Interceptor starting...");
await invocation.ProceedAsync();
await Console.Out.WriteLineAsync("Interceptor ended...");
}
}
運(yùn)行如下
,
可以看到攔截器這時(shí)候是異步方法,并且可以明顯看到注入被簡(jiǎn)化了。
大家可以考慮下為什么 services.ConfigureCastleDynamicProxy()
一定要在BuildServiceProvider()之前,其他注入之后
進(jìn)階版本
進(jìn)階 版本這里我們不再詳細(xì)描述,直接看源碼 https://gitee.com/gainorloss_259/microsoft-castle.git
主要解決的問題是castle 攔截器不支持 依賴ioc的服務(wù)
使用偽代碼如下
public class SampleService : ISampleService
{
[CatchLoggingInterceptor]
[Interceptor(typeof(LoggingInterceptor))]//第二種使用方式
public virtual Task<string> ShowAsync()
{
Console.WriteLine(nameof(ShowAsync));
return Task.FromResult(nameof(ShowAsync));
}
}
//定義攔截器
internal class LoggingInterceptor : InterceptorBase
{
private readonly ILogger<LoggingInterceptor> _logger;
public LoggingInterceptor(ILogger<LoggingInterceptor> logger)
{
_logger = logger;
}
protected override async ValueTask InterceptAsync(IAsyncInvocation invocation)
{
await Console.Out.WriteLineAsync(nameof(LoggingInterceptor));
await invocation.ProceedAsync();
}
}
總結(jié)
以上 整合的核心方案及細(xì)節(jié)已經(jīng)介紹完畢了,接下來有時(shí)間的話可以出一篇對(duì)本整合做性能測(cè)試的文章;
AOP 是一個(gè)很強(qiáng)大的東西,我們基本已經(jīng)完成了一個(gè)比較普適、比較易用的aop底層整理。接下來我們可以做很多東西,比如 事務(wù)攔截器、冪等攔截器、重試攔截器、緩存攔截器等等
打好基礎(chǔ),后續(xù)可以自己去實(shí)現(xiàn)。
這里還有幾個(gè)問題 ,大家可以思考下
- 我們?nèi)绾文苷蟽煞N攔截器 既可以傳一些常量又不影響我們的服務(wù)注入攔截器
- 攔截器是否可以再套用攔截器
- 假設(shè)我們?cè)偃罩緮r截器上打了日志攔截器 會(huì)怎么樣
這些都是一些比較有意思的問題,相信這些問題的思考會(huì)讓大家對(duì)動(dòng)態(tài)代理的理解更深,并可以靈活的將其用到自己的項(xiàng)目中。文章來源:http://www.zghlxwxcb.cn/news/detail-786374.html
源碼及聲明
當(dāng)前示例代碼已傳至 https://gitee.com/gainorloss_259/microsoft-castle.git
如轉(zhuǎn)載請(qǐng)注明出處,謝謝!文章來源地址http://www.zghlxwxcb.cn/news/detail-786374.html
到了這里,關(guān)于聊一聊如何整合Microsoft.Extensions.DependencyInjection和Castle.Core(完結(jié)篇)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!