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

这篇具有很好参考价值的文章主要介绍了聊一聊如何整合Microsoft.Extensions.DependencyInjection和Castle.Core(完结篇)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

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

基础版

还是先上代码, 这是基础版本我们要达成的目标,仅需定义一个特性即可完成拦截的目标

/// <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);
    ......
}

如上是我们定义的拦截器基类,我们想要达到的目标是,只要继承该基类,并覆写InterceptAsync 方法即可实现具有特定功能的拦截类,而容器会自动代理到该拦截类,实现拦截。
这里要感谢 https://github.com/stakx/DynamicProxy.AsyncInterceptor 的作者,该库采用 MIT 的许可使用协议,我们可以直接参考使用。
接下来,是重头戏,考虑到易用性,我们以 Microsoft.Extension.DependencyInjection 为基本库,实现一个扩展类,用于实现拦截器功能。
代码如下:

public static class CastleServiceCollectionExtensions
{
    public static IServiceCollection ConfigureCastleDynamicProxy(this IServiceCollection services)
    {
        services.TryAddSingleton<ProxyGenerator>(sp => new ProxyGenerator());
        //TODO:1.从IServiceCollection中获取 方法定义InterceptorBaseAttribute特性子类的ServiceDescriptor

        //TODO:2.逐个处理,获取每个ServiceDescriptor中的ServiceType,识别是具体类还是接口,然后获取InterceptorBaseAttribute特性子类的实例
        //作为拦截器,借用proxyGenerator 去创建对应的代理然后添加到IServiceCollection中

        //TODO:3 移除原始对应的ServiceType注册
        return services;
    }
}

在注释中我们简单描述了该扩展方法的实现过程,我们采用移花接木的方式替换掉原有ServiceType的注册,将代理对象注册为ServiceType的实现即可。

第一步我们这么实现

var descriptors = services.Where(svc =>svc.ServiceType.GetMethods()
    .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
    伪代码如下
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和 实现对象实例
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和 实现工厂方法
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比较雷同,因为拿到 实例和通过委托传入IServiceProvider拿到实例,其实结果是相似的,最终我们都使用工厂注入的形式 生成新的 ServiceDescriptor services.AddTransinet(descriptor.ServiceType, implementationFactory);
最后一步 移除即可
伪代码如下
services.Remove(descriptor);

改造一下之前的代码并测试

 var services = new ServiceCollection();
services.AddLogging();//此处添加日志服务 伪代码 以便获取ILogger<SampleService>
services.TryAddTransient<SampleService>();
services.TryAddTransient<ISampleService, SampleService>();
services.ConfigureCastleDynamicProxy();//一定要在最后,不然会有些服务无法代理到 2024-1-13 13:53:05
var sp = services.BuildServiceProvider();

var proxy = sp.GetRequiredService<SampleService>();
var name = await proxy.ShowAsync();

/// <summary>
/// 异常捕获、日志记录和耗时监控 拦截器 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...");
   }
}

运行如下
聊一聊如何整合Microsoft.Extensions.DependencyInjection和Castle.Core(完结篇)
,聊一聊如何整合Microsoft.Extensions.DependencyInjection和Castle.Core(完结篇)

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

进阶版本

进阶 版本这里我们不再详细描述,直接看源码 https://gitee.com/gainorloss_259/microsoft-castle.git
主要解决的问题是castle 拦截器不支持 依赖ioc的服务
使用伪代码如下

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();
   }
}

总结

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

  1. 我们如何能整合两种拦截器 既可以传一些常量又不影响我们的服务注入拦截器
  2. 拦截器是否可以再套用拦截器
  3. 假设我们再日志拦截器上打了日志拦截器 会怎么样

这些都是一些比较有意思的问题,相信这些问题的思考会让大家对动态代理的理解更深,并可以灵活的将其用到自己的项目中。

源码及声明

当前示例代码已传至 https://gitee.com/gainorloss_259/microsoft-castle.git
如转载请注明出处,谢谢!文章来源地址https://www.toymoban.com/news/detail-786374.html

到了这里,关于聊一聊如何整合Microsoft.Extensions.DependencyInjection和Castle.Core(完结篇)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 聊一聊 TLS/SSL

    哈喽大家好,我是咸鱼 当我们在上网冲浪的时候,会在浏览器界面顶部看到一个小锁标志,或者网址以 \\\"https://\\\" 开头 这意味着我们正在使用 TLS/SSL 协议进行安全通信。虽然它可能看起来只是一个小小的锁图标和一个 “https” ,但实际上,这个协议在保护我们的在线隐私和安

    2024年02月08日
    浏览(44)
  • 聊一聊AIGC

    “UGC不存在了”——借鉴自《三体》 ChatGPT 的横空出世将一个全新的概念推上风口——AIGC( AI Generated Content)。 GC即创作内容(Generated Content),和传统的UGC、PGC,OGC不同的是,AIGC的创作主体由人变成了人工智能。 xGC PGC:Professionally Generated Content,专业生产内容 UGC:User G

    2024年02月10日
    浏览(53)
  • 聊一聊模板方法模式

    统一抽取,制定规范; 模板方法模式,又叫模板模式,属于23种设计模式中的 行为型模式 。在抽象类中公开定义了执行的方法,子类可以按需重写其方法,但是要以抽象类中定义的方式调用方法。总结起来就是: 定义一个操作的算法结构,而将一些步骤延迟到子类中。在不

    2024年02月04日
    浏览(41)
  • 聊一聊Vue和Ts

    1 前言 Vue3 已经正式发布了一段时间了,各种生态已经成熟。最近使用 taro+vue3 重构冷链的小程序,经过了一段时间的开发和使用,有了一些自己的思考。 总的来说,Vue3 无论是在底层原理还是在实际开发过程中,都有了很大的进步。 从源码层面来说,使用 Proxy 代替 Object.d

    2023年04月08日
    浏览(62)
  • 聊一聊适配器模式

    接口不能用?行,我帮你适配 适配器模式(Adapter),是23种设计模式中的 结构型模式 之一;它就像我们电脑上接口不够时,需要用到的拓展坞,起到转接的作用。它可以将新的功能和原先的功能连接起来,使由于需求变动导致不能用的功能,重新利用起来。 上图的Mac上,只

    2024年02月04日
    浏览(43)
  • 聊一聊mysql中的间隙锁

    间隙锁在mysql中经常使用到,今天就聊一聊mysql的间隙锁的内容。 间隙锁是为了解决幻读的问题,并且在当前读的场景下解决的。 当前读包含:update,delete,insert,select…lock in share mode,select…for update 一基本概念 1、行锁:给某一行进行加锁 2、间隙锁:两个值之间的间隙,为解

    2024年02月12日
    浏览(31)
  • 聊一聊nginx中KeepAlive的设置

    之前工作中遇到一个KeepAlive的问题,现在把它记录下来,场景是这样的: 从上图可以看出,用户通过Client访问的是LVS的VIP, VIP后端挂载的RealServer是Nginx服务器。 Client可以是浏览器也可以是一个客户端程序。一般情况下, 这种架构不会出现问题,但是如果Client端把请求发送给

    2024年02月01日
    浏览(46)
  • 聊一聊Java抽象同步队列AQS

    AQS是锁的底层支持 由该图可以看到,AQS是一个FIFO的双向队列,其内部通过节点head和tail记录队首和队尾元素,队列元素的类型为Node。其中Node中的thread变量用来存放进入AQS队列里面的线程;Node节点内部的SHARED用来标记该线程是获取共享资源时被阻塞挂起后放入AQS队列的,EX

    2024年02月16日
    浏览(35)
  • C++面试八股文:聊一聊指针?

    某日二师兄参加XXX科技公司的C++工程师开发岗位第17面: 面试官:聊一聊指针? 二师兄:好的。 面试官:你觉得指针本质上是什么? 二师兄:这要从内存地址开始说起了。如果有一块容量是1G的内存,假设它的地址是从 0x00000000 到 0x3fffffff ,每一个字节都对应一个地址。当

    2024年02月09日
    浏览(29)
  • 聊一聊springboot项目配置参数的三个来源

    大家好,我是G探险者,今天我们聊一聊,springboot的参数的三个来源。 平时我们部署项目后,难免都会进行一下配置修改,有的是在修改环境变量,有的是直接通过命令行参数进行修改,有的是直接改,配置文件,那么这三种不同来源的参数修改,都是如何生效的,以及他们

    2024年02月09日
    浏览(24)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包