Autofac.Annotation框架是我用.netcore写的一个注解式DI框架,基于Autofac参考 Spring注解方式所有容器的注册和装配,切面,拦截器等都是依赖标签来完成。

开源地址:https://github.com/yuzd/Autofac.Annotation

本期讲的是最新重构的功能,这个功能也是赋予了这个框架的无限可能,也是我觉得设计的比较好的地方, 今天来说说我是怎么设计的

切面和拦截器介绍

拦截器是什么?

可以帮助我们方便在执行目标方法的

  • 前(Before)
  • 后(After)
  • 返回值时(AfterReturn)
  • 抛错误时(AfterThrowing)
  • 环绕(Around)

简单示例:

    //自己实现一个拦截器
    public class TestHelloBefore:AspectBefore
    {
        public override Task Before(AspectContext aspectContext)
        {
            Console.WriteLine("TestHelloBefore");
            return Task.CompletedTask;
        }
    }
    
    [Component]
    public class TestHello
    {         [TestHelloBefore]//打上拦截器
        public virtual void Say()
        {
            Console.WriteLine("Say");
        }
    }

先执行 TestHelloBefor的Before方法再执行你的Say方法

更多使用示例请查看 Aspect拦截器

切面是什么?

定义一个切面(根据筛选器去实现满足条件的多个类的多个方法的“拦截器”

简单示例:

    [Component]
    public class ProductController
    {
        public virtual string GetProduct(string productId)
        {
            return "GetProduct:" + productId;
        }
        
        public virtual string UpdateProduct(string productId)
        {
            return "UpdateProduct:" + productId;
        }
    }
    
    [Component]
    public class UserController
    {
        public virtual string GetUser(string userId)
        {
            return "GetUser:" + userId;
        }
        
        public virtual string DeleteUser(string userId)
        {
            return "DeleteUser:" + userId;
        }
    }
    
    // *Controller 代表匹配 只要是Controller结尾的类都能匹配
    // Get* 代表上面匹配成功的类下 所以是Get打头的方法都能匹配
    [Pointcut(Class = "*Controller",Method = "Get*")]
    public class LoggerPointCut
    {
        [Around]
        public async Task Around(AspectContext context,AspectDelegate next)
        {
            Console.WriteLine("PointcutTest1.Around-start");
            await next(context);
            Console.WriteLine("PointcutTest1.Around-end");
        }         [Before]
        public void Before()
        {
            Console.WriteLine("PointcutTest1.Before");
            
        }
        
        [After]
        public void After()
        {
            Console.WriteLine("PointcutTest1.After");
            
        }
        
        [AfterReturn(Returing = "value1")]
        public void AfterReturn(object value1)
        {
            Console.WriteLine("PointcutTest1.AfterReturn");
        }
        
        [AfterThrows(Throwing = "ex1")]
        public void Throwing(Exception ex1)
        {
            Console.WriteLine("PointcutTest1.Throwing");
        }       
    }

更多示例请查看 Pointcut切面编程

如何实现的

分为3步

  • 1.搜集拦截算子(比如Before/After等这个我们叫算子)
  • 2.构造拦截器链(按照上面图的方式把算子链接起来)
  • 3.生成代理类代理目标方法去执行上面构造的拦截器链

1.搜集拦截算子

因为拦截器的使用是约定了要继承 AspectInvokeAttribute

    /// <summary>
    ///     AOP拦截器 默认包含继承关系
    /// </summary>
    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
    public class AspectInvokeAttribute : Attribute
    {
        /// <summary>
        ///     排序 值越低,优先级越高
        /// </summary>
        public int OrderIndex { get; set; }         /// <summary>
        ///     分组名称
        /// </summary>
        public string GroupName { get; set; }
    }

image

这一组注解是暴露给外部使用,来搜集哪些类的哪些方法需要增强

接下来需要去针对性去实现每一种增强器要做的事情

定义一个增强器接口IAdvice

    internal interface IAdvice
    {
        /// <summary>
        ///  拦截器方法
        /// </summary>
        /// <param name="aspectContext">执行上下文</param>
        /// <param name="next">下一个增强器</param>
        /// <returns></returns>
        Task OnInvocation(AspectContext aspectContext, AspectDelegate next);
    }

image

Before增强器

    /// <summary>
    /// 前置增强器
    /// </summary>
    internal class AspectBeforeInterceptor : IAdvice
    {
        private readonly AspectBefore _beforeAttribute;         public AspectBeforeInterceptor(AspectBefore beforeAttribute)
        {
            _beforeAttribute = beforeAttribute;
        }         public async Task OnInvocation(AspectContext aspectContext, AspectDelegate next)
        {
            //先执行Before逻辑
            await this._beforeAttribute.Before(aspectContext);
            //在走下一个增强器
            await next.Invoke(aspectContext);
        }
    }

After增强器

    /// <summary>
    /// 后置增强器
    /// </summary>
    internal class AspectAfterInterceptor : IAdvice
    {
        private readonly AspectAfter _afterAttribute;
        private readonly bool _isAfterAround;         public AspectAfterInterceptor(AspectAfter afterAttribute, bool isAfterAround = false)
        {
            _afterAttribute = afterAttribute;
            _isAfterAround = isAfterAround;
        }         public async Task OnInvocation(AspectContext aspectContext, AspectDelegate next)
        {
            try
            {
                if (!_isAfterAround) await next.Invoke(aspectContext);
            }
            finally
            {
                //不管成功还是失败都会执行的 
                 await this._afterAttribute.After(aspectContext, aspectContext.Exception ?? aspectContext.ReturnValue);
            }
        }
    }

环绕增强器


    /// <summary>
    /// 环绕返回拦截处理器
    /// </summary>
    internal class AspectAroundInterceptor : IAdvice
    {
        private readonly AspectArround _aroundAttribute;
        private readonly AspectAfterInterceptor _aspectAfter;
        private readonly AspectAfterThrowsInterceptor _aspectThrows;         public AspectAroundInterceptor(AspectArround aroundAttribute, AspectAfter aspectAfter, AspectAfterThrows chainAspectAfterThrows)
        {
            _aroundAttribute = aroundAttribute;
            if (aspectAfter != null)
            {
                _aspectAfter = new AspectAfterInterceptor(aspectAfter, true);
            }             if (chainAspectAfterThrows != null)
            {
                _aspectThrows = new AspectAfterThrowsInterceptor(chainAspectAfterThrows, true);
            }
        }         public async Task OnInvocation(AspectContext aspectContext, AspectDelegate next)
        {
            Exception exception = null;
            try
            {
                if (_aroundAttribute != null)
                {
                    await _aroundAttribute.OnInvocation(aspectContext, next);
                    return;
                }
            }
            catch (Exception ex)
            {
                exception = ex;
            }
            finally
            {
                if (exception == null && _aspectAfter != null) await _aspectAfter.OnInvocation(aspectContext, next);
            }             try
            {
                if (exception != null && _aspectAfter != null)
                {
                    await _aspectAfter.OnInvocation(aspectContext, next);
                }                 if (exception != null && _aspectThrows != null)
                {
                    await _aspectThrows.OnInvocation(aspectContext, next);
                }
            }
            finally
            {
                if (exception != null) throw exception;
            }
        }
    }

返回值增强器

    /// <summary>
    /// 后置返值增强器
    /// </summary>
    internal class AspectAfterReturnInterceptor : IAdvice
    {
        private readonly AspectAfterReturn _afterAttribute;         public AspectAfterReturnInterceptor(AspectAfterReturn afterAttribute)
        {
            _afterAttribute = afterAttribute;
        }         public async Task OnInvocation(AspectContext aspectContext, AspectDelegate next)
        {
            await next.Invoke(aspectContext);             //执行异常了不执行after 去执行Throw
            if (aspectContext.Exception != null)
            {
                return;
            }             if (_afterAttribute != null)
            {
                await this._afterAttribute.AfterReturn(aspectContext, aspectContext.ReturnValue);
            }
        }
    }

异常返回增强器

    /// <summary>
    /// 异常返回增强器
    /// </summary>
    internal class AspectAfterThrowsInterceptor : IAdvice
    {
        private readonly AspectAfterThrows _aspectThrowing;
        private readonly bool _isFromAround;
        public AspectAfterThrowsInterceptor(AspectAfterThrows throwAttribute, bool isFromAround = false)
        {
            _aspectThrowing = throwAttribute;
            _isFromAround = isFromAround;
        }         public async Task OnInvocation(AspectContext aspectContext, AspectDelegate next)
        {
            try
            {
                if (!_isFromAround) await next.Invoke(aspectContext);
            }
            finally
            {
                //只有目标方法出现异常才会走 增强的方法出异常不要走
                if (aspectContext.Exception != null)
                {
                    Exception ex = aspectContext.Exception;
                    if (aspectContext.Exception is TargetInvocationException targetInvocationException)
                    {
                        ex = targetInvocationException.InnerException;
                    }                     if (ex == null)
                    {
                        ex = aspectContext.Exception;
                    }                     var currentExType = ex.GetType();                     if (_aspectThrowing.ExceptionType == null || _aspectThrowing.ExceptionType == currentExType)
                    {
                        await _aspectThrowing.AfterThrows(aspectContext, aspectContext.Exception);
                    }
                }
            }
        }
    }

2. 组装增强器们成为一个调用链

image

每一个node的有三个信息,如下

    /// <summary>
    /// 拦截node组装
    /// </summary>
    internal class AspectMiddlewareComponentNode
    {
        /// <summary>
        /// 下一个
        /// </summary>
        public AspectDelegate Next;         /// <summary>
        /// 执行器
        /// </summary>
        public AspectDelegate Process;         /// <summary>
        /// 组件
        /// </summary>
        public Func<AspectDelegate, AspectDelegate> Component;
    }

采用LinkedList来构建我们的拉链式调用, 我们把上面的每个增强器作为一个个middeware,添加进来。

    internal class AspectMiddlewareBuilder
    {
        private readonly LinkedList<AspectMiddlewareComponentNode> Components = new LinkedList<AspectMiddlewareComponentNode>();         /// <summary>
        /// 新增拦截器链
        /// </summary>
        /// <param name="component"></param>
        public void Use(Func<AspectDelegate, AspectDelegate> component)
        {
            var node = new AspectMiddlewareComponentNode
            {
                Component = component
            };             Components.AddLast(node);
        }         /// <summary>
        /// 构建拦截器链
        /// </summary>
        /// <returns></returns>
        public AspectDelegate Build()
        {
            var node = Components.Last;
            while (node != null)
            {
                node.Value.Next = GetNextFunc(node);
                node.Value.Process = node.Value.Component(node.Value.Next);
                node = node.Previous;
            }             return Components.First.Value.Process;
        }         /// <summary>
        /// 获取下一个
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        private AspectDelegate GetNextFunc(LinkedListNode<AspectMiddlewareComponentNode> node)
        {
            return node.Next == null ? ctx => Task.CompletedTask : node.Next.Value.Process;
        }
    }

然后build方法会构建成一个一层嵌套一层的pipeline管道(一个委托)

image

更多关于这种设计模式更多信息请参考我另外一篇文章: 中间件(middlewware)模式

按照我们的需求构建的完整执行示意图如下:

单个拦截器或者切面

image
多个拦截器或者切面

image

生成代理类代理目标方法去执行上面构造的拦截器链

这一步就简单了,如果检测到目标有打拦截器注解,则会给这个类动态创建一个proxy类,

生成代理类用的是castle.core的dynamic组件

默认的是Class+virtual的方式对目标方法进行拦截

image

注意:考虑到性能,在项目启动的时候把构建好进行缓存,然后再拦截器里面使用

好了,拦截器和切面介绍到此,更多教程请参考项目wiki(教程很详细哦,别忘记给个star)

https://github.com/yuzd/Autofac.Annotation/wiki

关注公众号一起学习

Autofac实现拦截器和切面编程的更多相关文章

  1. SpringBoot09 自定义servlet、注册自定义的servlet、过滤器、监听器、拦截器、切面、webmvcconfigureradapter过时问题

    1 servlet简介 servlet是一种用于开发动态web资源的技术 参考博客:servlet基础知识     httpservlet详解 2 在springboot应用中添加servlet sp ...

  2. SpringBoot自定义servlet、注册自定义的servlet、过滤器、监听器、拦截器、切面、webmvcconfigureradapter过时问题

    [转]https://www.cnblogs.com/NeverCtrl-C/p/8191920.html 1 servlet简介 servlet是一种用于开发动态web资源的技术 参考博客:serv ...

  3. Autofac的切面编程实现

    *:first-child { margin-top: 0 !important; } .markdown-body>*:last-child { margin-bottom: 0 !impor ...

  4. C#使用Autofac实现控制反转IoC和面向切面编程AOP

    Autofac是一个.net下非常优秀,性能非常好的IOC容器(.net下效率最高的容器),加上AOP简直是如虎添翼.Autofac的AOP是通过Castle(也是一个容器)项目的核心部分实现的,名为 ...

  5. .net core autofac asyncinterceptor 异步拦截器开发

    autofac使用拦截器实现AOP,是基于Castle.Core的.然而Castle.Core并未提供原生异步支持.所以需要使用帮助类实现,这在autofac官方文档的已知问题中有详细说明: http ...

  6. .net core autofac asyncinterceptor 异步拦截器帮助包

    autofac使用拦截器实现AOP,是基于Castle.Core的.然而Castle.Core并未提供原生异步支持.所以需要使用帮助类实现,这在autofac官方文档的已知问题中有详细说明: http ...

  7. struts2-第二章-拦截器

    一,回顾 (1)默认action,404问题;<default-action-ref name="action 名称"/> (2)模块化,package,struts. ...

  8. 过滤器、拦截器和AOP的分析与对比

    目录 一.过滤器(Filter) 1.1 简介 1.2 应用场景 1.3 源码分析 二.拦截器(Interceptor) 2.1 简介 2.2 应用场景 2.2 源码分析 三.面向切面编程(AOP) ...

  9. 面试题:struts 拦截器和过滤器

    拦截器和过滤器的区别 过滤器是servlet规范中的一部分,任何java web工程都可以使用. 拦截器是struts2框架自己的,只有使用了struts2框架的工程才能用. 过滤器在url-patt ...

随机推荐

  1. Linux使用docker部署nacos

    官网地址:https://nacos.io/zh-cn/docs/quick-start-docker.html 先把sql文件导入到mysql中 我也放了基础的sql /* * Copyright ...

  2. 【LeetCode】814. Binary Tree Pruning 解题报告(Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 后序遍历 日期 题目地址:https://leetc ...

  3. Codeforces A. Orchestra

    A. Orchestra time limit per test 2 seconds memory limit per test 256 megabytes input standard input ...

  4. rsync 守护进程及实时同步

    目录 rsync 守护进程及实时同步 rsync简介 rsync特性 rsync应用场景 cp命令 scp命令 rsync的传输方式 rsync的传输模式 rsync实际使用 rsync命令 案例 r ...

  5. Wiener Filtering

    目录 基本 滤波的推导 特别的情况 特别的例子 Signals, Systems and Inference, Chapter 11: Wiener Filtering (mit.edu) 基本 在图 ...

  6. 解决opencv:AttributeError: 'NoneType' object has no attribute 'copy'

    情况一: 路径中有中文,更改即可 情况二:可以运行代码,在运行结束时显示 AttributeError: 'NoneType' object has no attribute 'copy' 因为如果是 ...

  7. 剖析Defi之Uinswap_0

    Uniswap是什么,不需要讲了吧.YYDS(永远嘀神) 介绍几个概念: 恒定乘积算法:可以简单看作X * Y = K,这里K(乘积)保持不变,所以叫恒定乘积算法,该函数是一个反曲线. 自动流动性协议 ...

  8. Ubuntu复习笔记-认识Linux

    本次复习基于\(Ubuntu20.04\)的发行版进行总结,目的是更好记录自己学习的\(Linux\). 认识Linux 学习\(Linux\)之前,需要搞懂几个概念,\(Linux\)桌面操作系统与 ...

  9. Ubuntu16.04下,erlang安装和rabbitmq安装步骤

    文章来源: Ubuntu16.04下,erlang安装和rabbitmq安装步骤 准备工作,先下载erlang和rabbitmq的安装包,注意他们的版本,版本不对可能会导致rabbitmq无法启动,这 ...

  10. sqlsugar freesql hisql 三个ORM框架性能测试对比

    hisql与目前比较流行的ORM框架性能测试对比 总体测试结果 插入记录数 hisql(耗时) sqlsugar(耗时) freesql(耗时) 5条 0.0107秒 0.0312秒 0.02675秒 ...