您现在的位置是:首页 >其他 >Spring源码阅读:AOP原理网站首页其他

Spring源码阅读:AOP原理

循环网络不循环 2024-06-17 11:27:04
简介Spring源码阅读:AOP原理

一、概述

以下便是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;
    }

    @Override
    public 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文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
">
    <bean id="logUtil" class="com.mashibing.aop.xml.util.LogUtil" ></bean>
    <bean id="myCalculator" class="com.mashibing.aop.xml.service.MyCalculator" ></bean>
    <aop:config>
        <aop:aspect ref="logUtil">
            <aop:pointcut id="myPoint" expression="execution( Integer com.mashibing.aop.xml.service.MyCalculator.*  (..))"/>
            <aop:before method="start" pointcut-ref="myPoint"></aop:before>
            <aop:after method="logFinally" pointcut-ref="myPoint"></aop:after>
        </aop:aspect>
    </aop:config>
</beans>

测试类:

    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

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。