一、概述

以下便是Spring Aop的流程,下面我将一一介绍下面的各个方法。

下面是流程中的主要方法。

二、测试代码

下面我将写一个例子介绍Spring Aop的流程。

被增强类:

public class MyCalculator {public Integer add(Integer i, Integer j) throws NoSuchMethodException {Integer result = i+j;return result;}public Integer sub(Integer i, Integer j) throws NoSuchMethodException {Integer result = i-j;return result;}public Integer mul(Integer i, Integer j) throws NoSuchMethodException {Integer result = i*j;return result;}public Integer div(Integer i, Integer j) throws NoSuchMethodException {Integer result = i/j;return result;}public Integer show(Integer i){System.out.println("show .....");return i;}@Overridepublic String toString() {return "super.toString()";}}

增强类:

public class LogUtil {@Pointcut("execution(public Integer com.mashibing.service.MyCalculator.*(Integer,Integer))")public void myPointCut(){}private int start(JoinPoint joinPoint){//获取方法签名Signature signature = joinPoint.getSignature();//获取参数信息Object[] args = joinPoint.getArgs();System.out.println("log---"+signature.getName()+"方法开始执行:参数是"+Arrays.asList(args));return 100;}public static void logFinally(JoinPoint joinPoint){Signature signature = joinPoint.getSignature();System.out.println("log---"+signature.getName()+"方法执行结束。。。。。over");}}

xml文件:

测试类:

public static void main(String[] args) throws Exception {ApplicationContext ac = new ClassPathXmlApplicationContext("aop.xml");MyCalculator bean = ac.getBean(MyCalculator.class);bean.add(1,1);}

三、AOP流程

整个 Spring AOP 源码,其实分为 3 块,我们会结合上面的示例,给大家进行讲解。

第一块就是前置处理,我们在创建 MyCalculator Bean 的前置处理中,会遍历程序所有的切面信息,然后将切面信息保存在缓存中

第二块就是后置处理,我们在创建 MyCalculator Bean 的后置处理器中,里面会做两件事情:

  • 获取 MyCalculator 的切面方法:首先会从缓存中拿到所有的切面信息,和 MyCalculator 的所有方法进行匹配,然后找到 MyCalculator 所有需要进行 AOP 的方法。
  • 创建 AOP 代理对象:结合 MyCalculator 需要进行 AOP 的方法,选择 Cglib 或 JDK,创建 AOP 代理对象。

第三块就是执行切面,通过“责任链 + 递归”,去执行切面。

(一)代码入口

finishBeanFactoryInitialization是整个aop执行流程的入口。

通过preInstantiateSingletons方法进入doGetBean

进入 doGetBean(),进入创建 Bean 的逻辑。

(二)前置处理

主要就是遍历切面,放入缓存。

进入前置处理的入口。

遍历找到该对象。

里面有我们想要的切面信息。

这里是重点!敲黑板!!!

  1. 我们会先遍历所有的类;
  2. 判断是否切面,只有切面才会进入后面逻辑;
  3. 获取每个 Aspect 的切面列表;
  4. 保存 Aspect 的切面列表到缓存 advisorsCache 中。

( 后置处理

主要就是从缓存拿切面,和 MyCalculator的方法匹配,并创建 AOP 代理对象。

进入 doCreateBean(),走下面逻辑。

这里是重点!敲黑板!!!

  1. 先获取MyCalculator类的所有切面列表;
  2. 创建一个 AOP 的代理对象。

这里是重点!敲黑板!!!

这里有 2 种创建 AOP 代理对象的方式,我们是选用 Cglib 来创建。

(四) 切面执行

通过 “责任链 + 递归”,执行切面和方法。

下面就是“执行切面”最核心的逻辑,简单说一下设计思路:

  1. 设计思路:采用递归 + 责任链的模式;
  2. 递归:反复执行 CglibMethodInvocation 的 proceed();
  3. 退出递归条件:interceptorsAndDynamicMethodMatchers 数组中的对象,全部执行完毕;
  4. 责任链:示例中的责任链,是个长度为 3 的数组,每次取其中一个数组对象,然后去执行对象的 invoke()。

第一次递归

数组的第一个对象是 ExposeInvocationInterceptor,执行 invoke(),注意入参是 CglibMethodInvocation。

里面啥都没干,继续执行 CglibMethodInvocation 的 process()。

第二次递归:

数组的第二个对象是 MethodAfterAdviceInterceptor,执行 invoke()。

第三次递归:

执行完上面逻辑,就会退出递归,我们看看 invokeJoinpoint() 的执行逻辑,其实就是执行主方法。

四、疑惑点

AOP增强器的执行顺序如下图,但是责任链的执行顺序却和下图的执行顺序相反的,那么这是为什么呢?因为责任链的执行虽然是相反的,但是它是递归调用的,因此返回结果的时候顺序又是反过来的,正好和下图相同。

参考文章:

76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖! – 知乎

https://www.cnblogs.com/xxkj/p/14094203.html

Spring-aop源码解析 – 简书

https://www.cnblogs.com/V1haoge/p/9560803.html

https://www.cnblogs.com/RunningSnails/p/17013513.html

https://www.cnblogs.com/Acaak/p/16828906.html