翼度科技»论坛 编程开发 .net 查看内容

聊一聊如何整合Microsoft.Extensions.DependencyInjection和Castle.Core(完

2

主题

2

帖子

6

积分

新手上路

Rank: 1

积分
6
前言
  1. 书接上回,上回我们了解了 castle 代理的一些缺点,本文将开始操作整合 Microsoft.Extension.Dependency和Castle,以让默认的容器可以支持拦截器
  2. 我们将以进阶的形式逐步完善我们的封装,以实现一个更方便易用、普适、高性能的基础设施库。
复制代码
基础版

还是先上代码, 这是基础版本我们要达成的目标,仅需定义一个特性即可完成拦截的目标
  1. /// <summary>
  2. ///
  3. /// </summary>
  4. public abstract class InterceptorBaseAttribute : Attribute, IInterceptor
  5. {
  6.     void IInterceptor.Intercept(IInvocation invocation)
  7.     {
  8.         var returnType = invocation.Method.ReturnType;
  9.         var builder = AsyncMethodBuilder.TryCreate(returnType);
  10.         if (builder != null)
  11.         {
  12.             var asyncInvocation = new AsyncInvocation(invocation);
  13.             var stateMachine = new AsyncStateMachine(asyncInvocation, builder, task: InterceptAsync(asyncInvocation));
  14.             builder.Start(stateMachine);
  15.             invocation.ReturnValue = builder.Task();
  16.         }
  17.         else
  18.         {
  19.             Intercept(invocation);
  20.         }
  21.     }
  22.     protected virtual void Intercept(IInvocation invocation) { }
  23.     protected abstract ValueTask InterceptAsync(IAsyncInvocation invocation);
  24.     ......
  25. }
复制代码
如上是我们定义的拦截器基类,我们想要达到的目标是,只要继承该基类,并覆写InterceptAsync 方法即可实现具有特定功能的拦截类,而容器会自动代理到该拦截类,实现拦截。
这里要感谢 https://github.com/stakx/DynamicProxy.AsyncInterceptor 的作者,该库采用 MIT 的许可使用协议,我们可以直接参考使用。
接下来,是重头戏,考虑到易用性,我们以 Microsoft.Extension.DependencyInjection 为基本库,实现一个扩展类,用于实现拦截器功能。
代码如下:
  1. public static class CastleServiceCollectionExtensions
  2. {
  3.     public static IServiceCollection ConfigureCastleDynamicProxy(this IServiceCollection services)
  4.     {
  5.         services.TryAddSingleton<ProxyGenerator>(sp => new ProxyGenerator());
  6.         //TODO:1.从IServiceCollection中获取 方法定义InterceptorBaseAttribute特性子类的ServiceDescriptor
  7.         //TODO:2.逐个处理,获取每个ServiceDescriptor中的ServiceType,识别是具体类还是接口,然后获取InterceptorBaseAttribute特性子类的实例
  8.         //作为拦截器,借用proxyGenerator 去创建对应的代理然后添加到IServiceCollection中
  9.         //TODO:3 移除原始对应的ServiceType注册
  10.         return services;
  11.     }
  12. }
复制代码
在注释中我们简单描述了该扩展方法的实现过程,我们采用移花接木的方式替换掉原有ServiceType的注册,将代理对象注册为ServiceType的实现即可。
第一步我们这么实现
  1. var descriptors = services.Where(svc =>svc.ServiceType.GetMethods()
  2.     .Any(i => i.GetCustomAttributes(false).Any(i => i.GetType().IsAssignableTo(typeof(InterceptorBaseAttribute))))).ToList();
复制代码
第二步的核心是 ServiceDescriptor 中 三种生成场景的分开处理,至于是哪三种场景可以看下我的第一篇文章  https://www.cnblogs.com/gainorloss/p/17961153

  • descriptor.ImplementationType 有值:已知ServiceType和ImplementationType
    伪代码如下
  1. implementationFactory = sp =>
  2. {
  3.     var generator = sp.GetRequiredService<ProxyGenerator>();
  4.     var interceptors = GetInterceptors(descriptor.ServiceType);//获取拦截器 galoS@2024-1-12 14:47:47
  5.     var proxy = descriptor.ServiceType.IsClass
  6.     ? generator.CreateClassProxy(descriptor.ServiceType,  interceptors.ToArray())
  7.     : generator.CreateInterfaceProxyWithoutTarget(descriptor.ServiceType, interceptors.ToArray());
  8.     return proxy;
  9. };
复制代码

  • descriptor.ImplementationInstance 有值:已知ServiceType和 实现对象实例
  1. implementationFactory = sp =>
  2. {
  3.     var generator = sp.GetRequiredService<ProxyGenerator>();
  4.     var interceptors = GetInterceptors(descriptor.ServiceType, sp);//获取拦截器 galoS@2024-1-12 14:47:47
  5.     var proxy = descriptor.ServiceType.IsClass
  6.     ? generator.CreateClassProxyWithTarget(descriptor.ServiceType, descriptor.ImplementationInstance, interceptors.ToArray())
  7.     : generator.CreateInterfaceProxyWithTarget(descriptor.ServiceType, descriptor.ImplementationInstance, interceptors.ToArray());
  8.     return proxy;
  9. };
复制代码

  • descriptor.ImplementationFactory 有值:已知ServiceType和 实现工厂方法
  1. implementationFactory = sp =>
  2. {
  3.     var generator = sp.GetRequiredService<ProxyGenerator>();
  4.     var interceptors = GetInterceptors(descriptor.ServiceType, sp);//获取拦截器 galoS@2024-1-12 14:47:47
  5.     var proxy = descriptor.ServiceType.IsClass
  6.     ? generator.CreateClassProxyWithTarget(descriptor.ServiceType, descriptor.ImplementationInstance, interceptors.ToArray())
  7.     : generator.CreateInterfaceProxyWithTarget(descriptor.ServiceType, descriptor.ImplementationInstance, interceptors.ToArray());
  8.     return proxy;
  9. };
复制代码
可以看到 2,3比较雷同,因为拿到 实例和通过委托传入IServiceProvider拿到实例,其实结果是相似的,最终我们都使用工厂注入的形式 生成新的 ServiceDescriptor services.AddTransinet(descriptor.ServiceType, implementationFactory);。
最后一步 移除即可
伪代码如下
services.Remove(descriptor);
改造一下之前的代码并测试
  1. var services = new ServiceCollection();
  2. services.AddLogging();//此处添加日志服务 伪代码 以便获取ILogger<SampleService>
  3. services.TryAddTransient<SampleService>();
  4. services.TryAddTransient<ISampleService, SampleService>();
  5. services.ConfigureCastleDynamicProxy();//一定要在最后,不然会有些服务无法代理到 2024-1-13 13:53:05
  6. var sp = services.BuildServiceProvider();
  7. var proxy = sp.GetRequiredService<SampleService>();
  8. var name = await proxy.ShowAsync();
  9. /// <summary>
  10. /// 异常捕获、日志记录和耗时监控 拦截器 2024-1-12 21:28:22
  11. /// </summary>
  12. [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
  13. public class CatchLoggingInterceptor : InterceptorBaseAttribute
  14. {
  15.    protected override async ValueTask InterceptAsync(IAsyncInvocation invocation)
  16.    {
  17.        //TODO:类注释所写的逻辑
  18.        await Console.Out.WriteLineAsync("Interceptor  starting...");
  19.        Console.WriteLine("Interceptor  starting...");
  20.        await invocation.ProceedAsync();
  21.        await Console.Out.WriteLineAsync("Interceptor  ended...");
  22.    }
  23. }
复制代码
运行如下

,

可以看到拦截器这时候是异步方法,并且可以明显看到注入被简化了。
大家可以考虑下为什么 services.ConfigureCastleDynamicProxy() 一定要在BuildServiceProvider()之前,其他注入之后
进阶版本

进阶 版本这里我们不再详细描述,直接看源码 https://gitee.com/gainorloss_259/microsoft-castle.git
主要解决的问题是castle 拦截器不支持 依赖ioc的服务
使用伪代码如下
  1. public class SampleService : ISampleService
  2. {
  3.    [CatchLoggingInterceptor]
  4.    [Interceptor(typeof(LoggingInterceptor))]//第二种使用方式
  5.    public virtual Task<string> ShowAsync()
  6.    {
  7.        Console.WriteLine(nameof(ShowAsync));
  8.        return Task.FromResult(nameof(ShowAsync));
  9.    }
  10. }
  11. //定义拦截器
  12. internal class LoggingInterceptor : InterceptorBase
  13. {
  14.    private readonly ILogger<LoggingInterceptor> _logger;
  15.    public LoggingInterceptor(ILogger<LoggingInterceptor> logger)
  16.    {
  17.        _logger = logger;
  18.    }
  19.    protected override async ValueTask InterceptAsync(IAsyncInvocation invocation)
  20.    {
  21.        await Console.Out.WriteLineAsync(nameof(LoggingInterceptor));
  22.        await invocation.ProceedAsync();
  23.    }
  24. }
复制代码
总结

以上 整合的核心方案及细节已经介绍完毕了,接下来有时间的话可以出一篇对本整合做性能测试的文章;
AOP 是一个很强大的东西,我们基本已经完成了一个比较普适、比较易用的aop底层整理。接下来我们可以做很多东西,比如 事务拦截器、幂等拦截器、重试拦截器、缓存拦截器等等
打好基础,后续可以自己去实现。
这里还有几个问题 ,大家可以思考下

  • 我们如何能整合两种拦截器 既可以传一些常量又不影响我们的服务注入拦截器
  • 拦截器是否可以再套用拦截器
  • 假设我们再日志拦截器上打了日志拦截器 会怎么样
这些都是一些比较有意思的问题,相信这些问题的思考会让大家对动态代理的理解更深,并可以灵活的将其用到自己的项目中。
源码及声明

当前示例代码已传至 https://gitee.com/gainorloss_259/microsoft-castle.git
如转载请注明出处,谢谢!

来源:https://www.cnblogs.com/gainorloss/Undeclared/17962327
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具