Spring Boot -- Spring AOP原理及简单实现

一、AOP基本概念

什么是AOP,AOP英语全名就是Aspect oriented programming,字面意思就是面向切面编程。面向切面的编程是对面向对象编程的补充,面向对象的编程核心模块是类,然而在AOP中核心模块是切面。切面实现了多种类型和对象的模块化管理,比如事物的管理。

上面的解释可以你还是看不懂,那么我们举个例子来说明AOP是来解决什么样的问题。我们都知道传统的OOP是自上而下的逻辑开发:

Spring Boot -- Spring AOP原理及简单实现

上面这张图形象生动了描述了我们通过浏览器访问一个接口的函数调用过程,我们发送的http请求首先会根据url匹配到对应的controller,然后controller会去调用对应的的service,service再去调用dao、然后将处理的结果返回给浏览器。

1.1、Filter、Interceptor、AOP

那么现在我们有一个需求,想记录发送http请求的客户端IP以及请求接口信息,最简单的方法就是我们在每一个controller方法中调用一个打印相关信息的函数,这样会存在一个问题,有多少接口,我们就会调用多少次日志打印函数。那么有没有一种简单的方法可以实现日志的记录呢,有当然有,我们可以通过Filter或者Interceptor实现请求拦截功能,记录日志信息。那么该有人问什么是Filter、Interceptor,那它们和我们将要说的AOP有什么区别?

AOP使用的主要是动态代理 , 过滤器使用的主要是函数回调;拦截器使用是反射机制 。一个请求过来 ,先进行过滤器处理,看程序是否受理该请求 。 过滤器放过后 , 程序中的拦截器进行处理 ,处理完后进入 被 AOP动态代理重新编译过的主要业务类进行处理 。

  • Filter:和框架无关,过滤器拦截的是URL,可以控制最初的http请求,但是更细一点的类和方法控制不了。
  • Interceptor:拦截器拦截的页是URL,拦截器有三个方法,相对于过滤器更加细致,有被拦截逻辑执行前、后等。
  • AOP: 面向切面拦截的是类的元数据(包、类、方法名、参数等) 相对于拦截器更加细致,而且非常灵活,拦截器只能针对URL做拦截,而AOP针对具体的代码,能够实现更加复杂的业务逻辑。

三者功能类似,但各有优势,从过滤器 》拦截器 》切面,拦截规则越来越细致,执行顺序依次是过滤器、拦截器、切面。一般情况下数据被过滤的时机越早对服务的性能影响越小,因此我们在编写相对比较公用的代码时,优先考虑过滤器,然后是拦截器,最后是AOP。比如权限校验,一般情况下,所有的请求都需要做登陆校验,此时就应该使用过滤器在最顶层做校验;针对日志记录,一般日志只会针对部分逻辑做日志记录,而且牵扯到业务逻辑完成前后的日志记录,因此使用过滤器不能细致地划分模块,此时应该考虑拦截器,然而拦截器也是依据URL做规则匹配,因此相对来说不够细致,因此我们会考虑到使用AOP实现,AOP可以针对代码的方法级别做拦截,很适合日志功能。

1.2、AOP中的一些概念

上面说了那么多题外话,你应该对AOP有了一个初步的理解,下面我们将更深入的介绍AOP,AOP是一种面向切面的编程思想。这些横切性问题,把它们抽象为一个切面,关注点在切面的编程。

 Spring Boot -- Spring AOP原理及简单实现

AOP主要应用在日志记录,权限验证,效率检查,事务管理中。我们首先来看一下Spring官方提供的一些有关AOP的基本概念:

1).通知(Advice): AOP 框架中的增强处理。通知描述了切面何时执行以及如何执行增强处理;通知类型,主要有以下几种:

  • Before :前置通知,在连接点方法前调用;对应Spring中@Before注解;
  • After :后置通知,在连接点方法后调用;对应Spring中的@After注解;
  • AfterReturning:返回通知,在连接点方法执行并正常返回后调用,要求连接点方法在执行过程中没有发生异常;对应Spring中的@AfterReturning注解;
  • AfterThrowing:异常通知,当连接点方法异常时调用;对应Spring中的@AfterThrowing注解;
  • Around:环绕通知,它将覆盖原有方法,但是允许你通过反射调用原有方法;对应Spring中的@Around注解;

2).连接点(join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出。在 Spring AOP 中,连接点总是方法的调用,可以说目标对象中的方法就是一个连接点;

3).切点(Pointcut): 就是连接点的集合;对应Spring中的@Pointcut注解;

4).切面(Aspect): 切面是通知和切点的结合;对应Spring中的注解@Aspect修饰的一个类;

5).目标对象(Target object):即被代理的对象;

6).代理对象(AOP proxy):包含了原始对象的代码和增强后的代码的那个对象;

Spring Boot -- Spring AOP原理及简单实现

我们利用上面这一张图来说一下目标对象和代理对象的关系,代理对象可以看作是目标对象的加强版,它是对目标对象中方法功能的一个扩充。代理对象的实现主要有两种,一种是基于jdk动态代理的,这要求目标对象必须是接口的实现;而另一种实现是基于cglib,即代理对象是继承自目标对象。

1.3、切点匹配表达式

切面是如何拦截到指定的类的元数据(包、类、方法名、参数等) 的呢,这是通过切点匹配表达式实现的。目前S支持的切点匹配表达式主要有以下几种:

  • execution:可以定义到的最小粒度是方法,修饰符,包名,类名,方法名,Spring AOP主要也是使用这个匹配表达式;
  • within:只能定义到类;例如@Pointcut(within(com.jnu.example.*))
  • this:当前生成的代理对象的类型匹配;
  • target:目标对象类型匹配;
  • args:只针对参数;
  • annotation:针对注解;

例如: execution (* com.sample.service..*. *(..))

整个表达式可以分为五个部分:

1、execution()::表达式主体;

2、第一个*号:表示返回类型, *号表示所有的类型;

3、包名:表示需要拦截的包名,包名后面的..,表明com.sample.service包、及其子包;

4、第二个*号:表示类名,*号表示所有的类;

5、*(..):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个点表示任何参数;

 关于Spring AOP注解的使用,我这里就不介绍了,网上有大量的博客介绍如何使用。我们接下来就带大家来实现一个类似within切点匹配表达式的效果。

二、Spring AOP简单实现



参考文章:

[1]浅析Spring AOP(一)——基本概念