C#中的AOP(面向切面编程)

484 阅读4分钟

C#中的AOP(面向切面编程)

概念

AOP:Aspect Oriented Programming的缩写,意为面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP思想的延续。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

OOP关注的是将需求功能划分为不同的并且相对独立、封装良好的类,依靠继承和多态来定义彼此的关系。AOP能够将通用需求功能从不相关的类中分离出来,很多类共享一个行为,一旦发生变化,不需要去修改很多类,只需要去修改这一个类即可。

AOP.png

OOP是为了将状态和行为进行模块化。上图是一个商场系统,我们使用OOP将该系统纵向分为订单管理、商品管理、库存管理模块。在该系统里面,我们要进行授权验证。像订单、商品、库存都是业务逻辑功能,但是这三个模块都需要一些共有的功能,比如说授权验证、日志记录等。我们不可能在每个模块里面都去写授权验证,而且授权验证也不属于具体的业务,它其实属于功能性模块,并且会横跨多个业务模块。可以看到这里是横向的,这就是所谓的切面。通俗的将,AOP就是将公用的功能给提取出来,如果以后这些公用的功能发生了变化,我们只需要修改这些公用功能的代码即可,其它的地方就不需要去更改了。所谓的切面,就是只关注通用功能,而不关注业务逻辑,而且不修改原有的类。

AOP的优势

将通用功能从业务逻辑中抽离出来,提高代码复用性,有利于后期的维护和扩展。 软件设计时,抽出通用功能(切面),有利于软件设计的模块化,降低软件架构的复杂度。

AOP的劣势

AOP的对OOP思想的一种补充,它无法单独存在。如果说单独使用AOP去设计一套系统是不可能的。在设计系统的时候,如果系统比较简单,那么可以只使用POP或者OOP来设计。如果系统很复杂,就需要使用AOP思想。首先要使用POP来梳理整个业务流程,然后根据POP的流程,去整理类和模块,最后在使用AOP来抽取通用功能。

AOP和OOP的区别

  • 面向目标不同:OOP是面向名词领域(抽象出来一个事物,比如学生、员工,这些都是名词)。AOP是面向动词领域(比如鉴权、记录日志,这些都是动作或行为)。
  • 思想结构不同:OOP是纵向的(以继承为主线,所以是纵向的)。AOP是横向的。
  • 注重方面不同:OOP是注重业务逻辑单元的划分,AOP偏重业务处理过程中的某个步骤或阶段。

POP、OOP、AOP三种思想是相互补充的。在一个系统的开发过程中,这三种编程思想是不可或缺的。

AOP的实现

1. DynamicProxy方式(通过Autofac)

2. Filter方式(继承ActionFilterAttribute, IExceptionFilter等)

    /// <summary>
    /// 全局异常过滤器
    /// </summary>
    public class LoggingExceptionFilter: IExceptionFilter
    {
        private IWebHostEnvironment HostingEnvironment { get; }

        public LoggingExceptionFilter(IWebHostEnvironment hostingEnvironment)
        {
            HostingEnvironment = hostingEnvironment;
        }

        public void OnException(ExceptionContext context)
        {
            // 记录日志
            Log.Error(context.Exception, "Unhandled exception captured.");

            // 处理特殊Exception
            if (context.Exception is ExcelValidationException)
            {
                var validationException = (ExcelValidationException)context.Exception;
                GeneralResponse response = new GeneralResponse
                {
                    StatusCode = ResponseStatusCodeConstant.Exception,
                    StatusMessage = validationException.ExcelValidationMessgaes.ToString(),
                    ReturnObj = validationException.ExcelValidationMessgaes
                };
                context.Result = new JsonResult(response);
            }
            else
            {
                GeneralResponse response = new GeneralResponse
                {
                    StatusCode = ResponseStatusCodeConstant.Exception,
                    StatusMessage = HostingEnvironment.IsDevelopment() ? context.Exception.ToString() : context.Exception.Message,
                    ReturnObj = HostingEnvironment.IsDevelopment() ? context.Exception.ToString() : context.Exception.Message
                };
                context.Result = new JsonResult(response);
            }
        }
    }

    // 注入
    services.AddScoped<LoggingExceptionFilter>();
    // 使用
    [ServiceFilter(typeof(LoggingExceptionFilter))]

3. Middleware方式

     // 中间件方式
     public static class InterceptHandler
     {
         public static IApplicationBuilder UseInterceptMiddleware(this IApplicationBuilder app)
         {
             return app.UseMiddleware<InterceptMiddlware>();
         }
     }

    public class InterceptMiddlware
    {
        private readonly RequestDelegate _next;
        
        public InterceptMiddlware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext context)
        {
            PreProceed(context);
            await _next(context);
            PostProceed(context);
        }

        private void PreProceed(HttpContext context)
        {
            Console.WriteLine($"{DateTime.Now} middleware invoke preproceed");
        }

        private void PostProceed(HttpContext context)
        {
            Console.WriteLine($"{DateTime.Now} middleware invoke postproceed");
        }
    }

ASP.net Core的常用AOP

  • AuthorizeAttribute 权限验证
  • IResourceFilter 资源缓存
  • IActionFilter 方法前后的记录
  • IResultFilter 结果生成前后扩展
  • IAlwaysRun 响应结果的补充
  • IExceptionFilter 异常处理

参考

  1. www.cnblogs.com/dotnet26101…
  2. developer.aliyun.com/article/665…
  3. www.cnblogs.com/atomy/p/124…