AOP in .NET

aop in .net

AOP是所有现代OOP语言开发框架中都会具备的东西,随着Spring框架的普及,这个东西已经被玩烂了。可是很多人仍旧处于知其然不知其所以然的状态。本文将基于.NET环境探讨实现AOP的底层原理。

文中部分代码样例截图摘自Matthew D. Groves的《AOP in .NET》,推荐大家购买阅读。

中间件与过滤器原理截图摘自微软官方文档,请查看文中链接。

本文主要分为以下部分:

  1. 基础概念
  2. ASP.NET Core框架内置的AOP
    1. 中间件
    2. 过滤器
  3. AOP in .NET
    1. 织入
    2. 代理模式
    3. PostSharp与动态代理的区别
    4. 动态代理模式
    5. Castle.DynamicProxy
    6. Autofac + Castle.DynamicProxy

基础概念

面向对象编程通过类的继承机制来复用代码,这在大多数情况下这很有用。但是随着软件系统的越来复杂,出现了一些通过OOP处理起来相当费力的关注点,比如:日志记录,权限控制,缓存,数据库事务提交等等。它们的处理逻辑分散于各个模块,各个函数之中,这违反了Don‘t Repeat Yourself以及关注度点分离原则,不利于后期的代码维护。所谓AOP(面向切面编程),就是将这些关注点,看作一个个切面,捕获这些切面并将其处理程序模块化的过程。

ASP.NET Core框架内置的AOP

在.ASP.NET Core框架中,微软内置了一些处理AOP逻辑的机制。可惜的是它内置的IoC框架,不支持语言级别的AOP。

中间件机制

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/write

ASP.NET Core框架本身就是由一系列中间件组成的,它本身内置的异常处理,路由转发,权限控制,也就是在上述图中的请求管道中实现的。所以我们也完全可以基于中间件机制,实现AOP。

以异常处理为例,我可以将try catch加入到next方法的前后,以捕获后续运行过程中未处理的异常,并进行统一处理。代码如下:

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

    public async Task Invoke(HttpContext context, IHostingEnvironment env,ILogger<ExceptionHandlerMiddleware> logger)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            logger.LogError(new EventId(ex.HResult), ex, ex.Message);
            await context.HandleExceptionAsync(ex, env.IsDevelopment());
        }
    }
}

过滤器机制

https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters

过滤器本质上是由路由中间件(Routing Middleware)的请求管道实现的,如下图所示。

开发者通过定义并注册相应的过滤器,就能基于这个请求管道,来处理对应的关注点。Asp.NET Core 的过滤器执行顺序如下图:

我们可以基于中间件或者过滤器机制,完成简单的开发。可惜的是,这些并不是语言级别的aop。asp.net core是一个开发框架,它为了方便你开发,给你内置了一些条条框框,你照着做确实能够解决大部分问题。但是脱离了它,该如何实现AOP呢?

AOP in .NET

下面我们开始真正进入主题。

织入

要实现AOP,关键在于织入(“weaving”)。如上图代码中,LogAspect被织入到BusinessModule1中,Mehtod1在执行前后,就会分别调用BeginMethod以及EndMethod方法来处理日志记录逻辑。LogAspect如果能够织入到所有需要日志记录的方法中,我们就将分散的日志处理代码模块化成了一个统一的切面处理程序:LogAspect。这就是AOP。实现织入的方式分为两种:编译时织入、运行时织入。

当你使用C#创建.NET项目时,该项目将被编译为CIL(也称为MSIL,IL和bytecode)作为程序集(DLL或EXE文件)。 下图说明了这个过程。然后,公共语言运行时(CLR)可以将CIL转换成真实的机器指令(通过称为即时编译的过程,或JIT)。

《aop in .net》

所谓编译时织入,就是对编译产生的CIL做手脚,通过修改编译好的CIL文件,来达到织入的效果,如下图所示。编译时织入可通过PostSharp实现。

运行时织入则是在程序运行时来完成的织入,一般是通过DynamicProxy(动态代理)程序(Castle.Core)配合IoC容器(Autofac,StructureMap等)来实现的。更多样例可查看:https://github.com/TylerBrinks/Snap

这两种织入模式各有利弊,下面总结几点:

  1. PostSharp是在编译时进行的,DynamicProxy在运行时进行。所以一个会增加编译时间,一个会降低运行效率。
  2. 由于PostSharp需要安装额外的编译程序,这意味着没有安装PostSharp的机器,无法正确编译你开发的程序。这不利于应用在开源项目中,也不利于部署CI/CD的自动化编译服务。
  3. PostSharp为收费的商业项目,需要付费使用。而动态织入所需的IoC框架,以及动态代理组件Castle.Core都是开源免费的。
  4. DynamicProxy必须使用IoC容器,对于UI对象或领域对象,并不适合或不可能通过容器获取实例。PostSharp没有这个问题。
  5. DynamicProxy比PostSharp更易于进行单元测试。
  6. DynamicProxy在运行时执行,因此在编译完成后,你仍可以通过修改配置文件来修改切面配置。PostSharp做不到这一点。
  7. DynamicProxy的拦截器被附加到类的所有方法中,而PostSharp能够更精准的拦截。
  8. PostSharp能够在static方法、private方法、属性中织入AOP,而DynamicProxy做不到这一点。

你可以根据自己的需要选择合适的织入方式,不过由于PostSharp为商业付费项目,我后面不再对其进行过多讲解,需要的朋友可自行阅读《AOP in .NET》中的相关内容,或查阅PostSharp官网。

本文后面将主要通过代码样例讲述如何基于动态代理实现运行时织入。

(未完待续)

原文地址:https://www.cnblogs.com/wswind/p/aop_in_dotnet.html

时间: 2024-10-22 11:18:20

AOP in .NET的相关文章

Spring框架之Spring AOP

一.基于注解管理的AOP 1.Spring配置文件 <!-- 配置自动扫描包,自动扫描Bean组件,切面类 --> <context:component-scan base-package="com.zhoujian.spring.anno,com.zhoujian.spring.test"> <!-- <context:include-filter type="annotation" expression="org.a

Spring AOP中pointcut expression表达式解析 及匹配多个条件

Pointcut 是指那些方法需要被执行"AOP",是由"Pointcut Expression"来描述的. Pointcut可以有下列方式来定义或者通过&& || 和!的方式进行组合. args() @args() execution() this() target() @target() within() @within() @annotation 其中 execution 是用的最多的,其格式为: execution(modifiers-pat

Spring AOP(面向切面示例)

什么是AOP?基本概念切面(aspect):横切关注点被模块化的特殊对象.通知(advice):切面必须要完成的工作.切面中的每个方向称之为通知.通知是在切面对象中的.目标(target):被通知的对象.代理(proxy):向目标对象应用通知后创建的对象. 连接点(joinpoint):目标对象的程序执行的某个特定位置.如某个方法调用前,调用后的位置.包括两个信息:1.目标程序的哪个方法?2.方法执行 前还是执行后?切点(pointcut):每个类会有多个连接点,AOP通过切点定位到特定的边接点

Spring AOP之Introduction(@DeclareParents)简介

Spring的文档上对Introduction这个概念和相关的注解@DeclareParents作了如下介绍: Introductions (known as inter-type declarations in AspectJ) enable an aspect to declare that advised objects implement a given interface, and to provide an implementation of that interface on be

3、Spring的AOP详解和案例

AOP(Aspect Oriented Programming),即面向切面编程. 1.OOP回顾 在介绍AOP之前先来回顾一下大家都比较熟悉的OOP(Object Oriented Programming).OOP主要是为了实现编程的重用性.灵活性和扩展性.它的几个特征分别是继承.封装.多态和抽象.OOP重点体现在编程架构,强调的是类之间的层次关系. 2.OOP缺陷 为了更好的说明OOP的概念,我们接下来讲一个OOP的实例,重点分析OOP存在哪些缺陷,以便更好的理解AOP的相关内容. 先看如下

spring aop 原理

http://blog.csdn.net/moreevan/article/details/11977115 Spring AOP 实现原理 2013-09-24 15:23 79554人阅读 评论(11) 收藏 举报  分类: spring(2)  版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] 什么是AOP AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)

Spring AOP进行日志记录

在java开发中日志的管理有很多种.我一般会使用过滤器,或者是Spring的拦截器进行日志的处理.如果是用过滤器比较简单,只要对所有的.do提交进行拦截,然后获取action的提交路径就可以获取对每个方法的调用.然后进行日志记录.使用过滤器的好处是可以自己选择性的对某一些方法进行过滤,记录日志.但是实现起来有点麻烦. 另外一种就是使用Spring的AOP了.这种方式实现起来非常简单,只要配置一下配置文件就可以了.可是这种方式会拦截下所有的对action的每个操作.使得效率比较低.不过想做详细日志

手把手教你写个AOP框架

Why AOP? AOP(Aspect-Oriented Programming),意思是面向切面编程.传统的OOP面向对象相当于站在一个上帝模式从上往下看,里面的一块块都是一个对象,由我任意组合:而AOP不同之处在于,他是以一个旁观者的身法,从"侧面"看整个系统模块,看看哪里可以见缝插针,将自己想要处理的一段义务逻辑编制进去. Code duplication is the ultimate code smell. It's a sign that something is very

使用方法拦截器MethodInterceptor和AOP统一处理log

对每个接口的请求记录log的方法有很多种,比如用filter.mvc interceptor.method interceptor等.如果需要记录请求消息的payload,前两种不适用.下面介绍第三种的实现方法. 第一步:引入包依赖 <span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"></span><pre name=&

Spring ——AOP

AOP是OOP的延续,是软件开发中的一个热点. AOP技术,是OOP补充.OOP引入封装,继承和多态建立一种对象层次结构模拟公共行为集合,而对从左到右的关系则显得无能为力.对于AOP则恰恰适应这种横切技术. 简单说,就与业务无关,却为了业务模块所共同调用的逻辑封装起来,便于减少系统重复代码,降低模块间耦合度,利用维护和可操作性 横切技术将软分为两部分:核心关注点和横切关注点:业务处理流程为核心关注,与之关系不大的是横切关注.如:系统中各处都相似的日志,事务,权限成为横切关注点.AOP作用是将核心