首页 > 编程语言 >【Spring AOP】【九】Spring AOP源码解析-拦截器/通知器链的执行过程

【Spring AOP】【九】Spring AOP源码解析-拦截器/通知器链的执行过程

时间:2023-02-22 23:46:04浏览次数:40  
标签:拦截器 proceed Spring AOP mi Object 通知 执行

1  前言

上一节我们说了通知器链的而顺序问题,那么这节我们该看看它的执行了。我们拿JDK代理的执行过程来看哈。

2  源码分析

2.1  方法通读

对于JDK动态代理的,那我们再回来看下当获取到要执行的通知器链后,要做什么呢,看源码:

// 创建方法调用器 并要执行的拦截器传进去
MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// 执行拦截器链  也就是说proceed()这个方法里会循环调用或者递归调用
retVal = invocation.proceed();

可以看到创建了ReflectiveMethodInvocation这个对象:

protected ReflectiveMethodInvocation(
            Object proxy, @Nullable Object target, Method method, @Nullable Object[] arguments,
            @Nullable Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {

        this.proxy = proxy;
        this.target = target;
        this.targetClass = targetClass;
        this.method = BridgeMethodResolver.findBridgedMethod(method);
        this.arguments = AopProxyUtils.adaptArgumentsIfNecessary(method, arguments);
        this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
    }

那我们重点看下它的proceed()方法:

/**
 * 走到这里我们应该清楚
 * interceptorsAndDynamicMethodMatchers 里边已经有了我们的拦截器并且是按顺序排好的
 */
public Object proceed() throws Throwable {
    /**
     * currentInterceptorIndex起始值是-1
     * 也就是说interceptorsAndDynamicMethodMatchers.size() - 1 表示拦截器都执行完了
     * 该执行目标方法了
     */
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return invokeJoinpoint();
    }
    // 拿出当前要执行的拦截器
    Object interceptorOrInterceptionAdvice =
            this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        // Evaluate dynamic method matcher here: static part will already have
        // been evaluated and found to match.
        InterceptorAndDynamicMethodMatcher dm =
                (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
        // 方法匹配判断 匹配到就执行拦截器的invoke方法 并把this传过去了 也就是执行器链 这样那边就可以继续proceed()执行下一个拦截器咯
        if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
            return dm.interceptor.invoke(this);
        }
        else {
            // 匹配失败 继续proceed() 到下一个
            return proceed();
        }
    }
    else {
        // 执行拦截器逻辑,并传递 this
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

大致流程就是:从第一个通知器开始执行,直到最后一个通知器然后执行目标方法,那我们接下来看看这5类通知器,我按照通知器类型的顺序来哈。

2.2  @Around环绕通知-AspectJAroundAdvice

public Object invoke(MethodInvocation mi) throws Throwable {
    if (!(mi instanceof ProxyMethodInvocation)) {
        throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
    }
    ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
    ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
    JoinPointMatch jpm = getJoinPointMatch(pmi);
    // 执行环绕通知的增强
    return invokeAdviceMethod(pjp, jpm, null, null);
}

顺便我们拿来我们例子:

@Around("myPointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
    System.out.println("around before");
    Object res = joinPoint.proceed();
    System.out.println("around after");
    return res;
}

这时候我们是不是控制台会打印出:around before

执行过过程图:

看我们的例子接下来执行joinPoint.proceed(); 也就是要执行我们实际的目标方法,joinPoint其实还是我们的通知器链,那我们proceed()是不是继续执行下一个通知器么?是不是就到了前置通知了。

2.3  @Before前置通知-MethodBeforeAdviceInterceptor

public Object invoke(MethodInvocation mi) throws Throwable {
    this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
    return mi.proceed();
}
@Override
public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
   invokeAdviceMethod(getJoinPointMatch(), null, null);
}

执行前置通知,然后继续proceed()继续下一个,这里我有三个前置通知器,所以跳过两个通知器哈,调到该我们的后置通知了。

2.4  @After后置通知-AspectJAfterAdvice

public Object invoke(MethodInvocation mi) throws Throwable {
        try {
            // 继续下一个通知器
            return mi.proceed();
        }
        finally {
            // 调用通知方法
            invokeAdviceMethod(getJoinPointMatch(), null, null);
        }
    }
@After("myPointCut()")
    public void myAfter(JoinPoint point) {
        System.out.println("myAfter");
    }

可以看到finally里才会执行后置通知,也就是继续下一个通知器即返回通知,当通知器都执行完以及目标方法执行完时才会执行finally即后置通知,那我问问你后置通知执行完代码回到哪里了?

2.5  @AfterReturning返回通知-AfterReturningAdviceInterceptor

public Object invoke(MethodInvocation mi) throws Throwable {
    Object retVal = mi.proceed();
    this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
    return retVal;
}

看返回通知,先执行下一个通知器即异常通知,当通知器都执行完以及目标方法执行完时才会执行finally即后置通知,那我问问你后置通知执行完代码又回到哪里了?

2.6  @AfterThrowing异常通知-AspectJAfterThrowingAdvice

public Object invoke(MethodInvocation mi) throws Throwable {
    try {
        return mi.proceed();
    }
    catch (Throwable ex) {
        if (shouldInvokeOnThrowing(ex)) {
            invokeAdviceMethod(getJoinPointMatch(), null, ex);
        }
        throw ex;
    }
}

异常通知,看也是执行下一个通知器,已经到底了,所以执行目标方法。到这里了,问问你自己,下一步该执行什么了?是不是到返回通知了。

继续后置通知以及环绕通知结束。

 ok到这里,我们的代理的一个执行过程就结束了,结合成代码可以看成这样:

// 环绕通知前
// 前置通知
try {
    // 返回通知
    try {
        // 目标方法
    } catch (Throwable var3) {
        // 异常通知
    }
} finally {
    // 后置通知
}
// 环绕通知后

3  小结

好了,到这里我们的代理执行过程也看的差不多了,整个AOP整体上的一个过程,也有了大致的理解,哪里有理解不对的地方欢迎指正哈,大家也可以试试多个Aspect的时候,通知器的顺序是怎么样的,执行的过程什么样的,加油。

标签:拦截器,proceed,Spring,AOP,mi,Object,通知,执行
From: https://www.cnblogs.com/kukuxjx/p/17146243.html

相关文章

  • C# AOP实现方式
    一、AOP概念  官方解释:AOP(Aspect-OrientedProgramming,面向切面编程),它是可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一......
  • Spring IOC官方文档学习笔记(十二)之基于Java的容器配置
    1.@Bean与@Configuration(1)标注于类之上的@Configuration注解与标注于方法之上的@Bean注解是支持基于Java的容器配置的核心,被@Bean注解标注的方法用于实例化bean并将其......
  • SpringBean的生命周期
    springBean的生命周期实例化阶段spring框架取出beanDefinition之前会判断信息进行判断当前bean的范围是否是单例,是否延迟加载,是否是factoryBean等,然后再通过反射进行实......
  • 【Spring AOP】【八】Spring AOP源码解析-拦截器/通知器链的执行顺序
    1 前言上节我们看过了代理对象执行方法的大致过程,我们留着通知器链的具体执行没说,这节我们先讲解一下通知器的执行顺序。通知器或者叫拦截器,叫法不一样,这里我们还是都......
  • spring-申明式事务
    一.什么是事务事务的原则是:处于事务中的sql代码块会保持一致状态,即要么都能改变数据库,要么都不能改变数据库ACID原则:原子性一致性隔离性多个业务可能操作同一个业......
  • ssm学习笔记23001-spring+mybatis修改删除和查询
    spring+mybatis修改删除和查询1、在UserMapper接口类中,创建接口在UserMapper接口类中,创建接口packagecom.wjw.mybatis.mapper;importcom.wjw.mybatis.pojo.User;......
  • spring5随笔
    1、Spring1.1、简介Spring:春天----->给软件行业带来了春天!2002,首次推出了Spring框架的雏形:interface21框架!Spring框架即以interface21框架为基础,经过重新设计,......
  • 【Spring 源码】001-环境准备:Spring模块梳理
    【Spring源码】001-环境准备:Spring模块梳理文章目录​​【Spring源码】001-环境准备:Spring模块梳理​​​​一、本节课程的目的​​​​二、出于信仰学习Spring的简史​​......
  • 整合mybatis-spring
    一.整合mybatis步骤:第一步:导入相关的jar包:junitmybatismysql数据库spring相关的aop植入mybatis-spring【新包,兼容mybatis和spring】<dependencies>......
  • 利用SpringBoot自动装配原理自定义starter
    在使用SpringBoot时候我们经常会碰到引入很多的starter的自动化配置,有了这些starter以后我们就可以很轻松的完成一个企业级的开发项目,很多小朋友就会对于starter很神奇,其实......