深入解析AOP调用链:递归与责任链模式的协同实现

本文将结合代码实现,深入剖析AOP(面向切面编程)调用链的核心原理。通过完整的示例代码和逐层解析,揭示递归调用与责任链模式如何协同构建方法调用栈的"洋葱模型"。


一、AOP调用链的核心设计

AOP调用链的核心是递归+责任链的协同机制:

  1. 责任链模式:每个拦截器作为处理节点,组成执行链
  2. 递归推进:通过状态索引控制链式调用顺序
  3. 洋葱模型:拦截器层层包裹目标方法,形成"前置→内层→后置"结构

关键数据结构 ​:MyMethodInvocation维护三个核心状态:

  • currentIndex:当前执行的拦截器位置
  • interceptorList:有序的拦截器链
  • target/method:目标方法信息

二、核心组件解析

1. 目标方法(业务逻辑)

java 复制代码
 static class CallChainBeanA {
     public void foo() {
         log.info("[CallChainBeanA] 执行了foo()方法(实际业务逻辑)");
     }
 }

作为调用链的最内层,包含实际业务逻辑,无任何拦截逻辑。

2. 拦截器实现(环绕通知)

拦截器A​(外层):

java 复制代码
 static class CallChainAdviceA implements MethodInterceptor {
      @Override
      public Object invoke(MethodInvocation invocation) throws Throwable {
          log.info("╭── [AdviceA] 前置执行 (堆栈深度: {})", Thread.currentThread().getStackTrace().length);
          log.debug("│   [AdviceA] 开始构建调用栈帧");

          // JVM在此处压入新的栈帧(invocation.proceed)
          Object result = invocation.proceed(); // 关键调用点:推进调用链

          log.debug("│   [AdviceA] 栈帧返回,恢复执行");
          log.info("╰── [AdviceA] 后置执行 (结果: {})", result);
          return result;
      }
  }

拦截器B​(中层):

java 复制代码
 static class CallChainAdviceB implements MethodInterceptor {
      @Override
      public Object invoke(MethodInvocation invocation) throws Throwable {
          log.info("│   ╭── [AdviceB] 前置执行 (堆栈深度: {})", Thread.currentThread().getStackTrace().length);
          log.debug("│   │   [AdviceB] 创建中间层栈帧");

          // 继续推进调用链(可能指向下一个拦截器或目标方法)
          Object result = invocation.proceed();

          log.debug("│   │   [AdviceB] 中间层栈帧返回");
          log.info("│   ╰── [AdviceB] 后置执行");
          return result;
      }
  }

设计要点​:

  • 每个拦截器必须调用proceed()推进链
  • 前置/后置逻辑形成对称结构
  • 日志缩进直观展示调用层级

三、递归引擎:MyMethodInvocation

核心方法proceed()实现链式推进:

java 复制代码
 @Override
 public Object proceed() throws Throwable {
     log.debug("├─ 推进调用链 [位置: {}/{}]", currentIndex, interceptorList.size());

     // 递归基:所有拦截器已执行完毕,执行目标方法
     if (currentIndex >= interceptorList.size()) {
         log.info("│   ╭── 执行目标方法 [调用栈深度: {}]", Thread.currentThread().getStackTrace().length);
         log.debug("│   │   ▌ 反射调用: {}.{}()",
                 target.getClass().getSimpleName(), method.getName());

         long startTime = System.nanoTime();
         Object result = invokeTargetMethod();  // 实际的方法调用点
         long duration = System.nanoTime() - startTime;

         log.debug("│   │   ▌ 执行时间: {} ns", duration);
         log.info("│   ╰── 目标方法完成 [结果: {}]", result);
         return result;
     }

     // 获取当前拦截器
     MethodInterceptor interceptor = interceptorList.get(currentIndex);
     log.debug("│   ├─ 获取拦截器 [{}]: {}", currentIndex,
             interceptor.getClass().getSimpleName());

     // 状态推进:索引+1(准备进入下一层递归)
     currentIndex++;

     // 关键递归步骤:调用拦截器的invoke方法(将自身作为参数传入)
     log.debug("│   │   ╭── 进入拦截器调用 [深度提升]");
     Object result = interceptor.invoke(this);
     log.debug("│   │   ╰── 拦截器返回 [恢复调用链状态]");

     return result;
 }

递归过程示例​:
返回结果 返回结果 返回结果 返回结果 proceed index=2 执行目标方法


四、调用链执行全流程分析

1. 初始化阶段

java 复制代码
// 创建目标对象
CallChainBeanA beanA = new CallChainBeanA();

// 构建拦截器链(执行顺序:A→B)
List<MethodInterceptor> interceptors = List.of(
    new CallChainAdviceA(), 
    new CallChainAdviceB()
);

// 创建调用入口
MyMethodInvocation invocation = new MyMethodInvocation(
    beanA, fooMethod, new Object[0], interceptors
);

2. 调用链执行日志分析

复制代码
╭── [AdviceA] 前置执行 (堆栈深度: 4)
│   ╭── [AdviceB] 前置执行 (堆栈深度: 6)
│   ╭── 执行目标方法 [调用栈深度: 7]
│   │   [CallChainBeanA] 执行了foo()方法(实际业务逻辑)
│   ╰── 目标方法完成 [结果: null]
│   ╰── [AdviceB] 后置执行
╰── [AdviceA] 后置执行

栈帧变化示意图​:
AdviceA 前置日志 AdviceB 前置日志 目标方法 后置日志 后置日志


五、关键技术原理

  1. 双栈协同机制

    • JVM方法栈:维护原生方法调用关系
    • 调用链栈:通过currentIndex记录自定义状态
    java 复制代码
    // 在拦截器中获取实际栈深度
    Thread.currentThread().getStackTrace().length
  2. 递归的终止条件

    java 复制代码
    if (currentIndex >= interceptorList.size()) {
        // 递归基:执行目标方法
        return method.invoke(target, args);
    }
  3. 异常处理机制

    java 复制代码
     private Object invokeTargetMethod() throws Throwable {
         try {
             return method.invoke(target, args);
         } catch (InvocationTargetException e) {
             throw e.getTargetException();  // 解包原始异常
         } catch (Exception e) {
             throw new RuntimeException("调用目标方法失败", e);
         }
     }

六、设计模式应用

  1. 责任链模式

    • 拦截器实现统一接口MethodInterceptor
    • 链式传递执行权
  2. 递归模式

    • 状态控制:currentIndex
    • 递归步骤:interceptor.invoke(this)
    • 终止条件:执行目标方法
  3. 代理模式

    • 所有方法调用通过MethodInvocation对象转发
    • 拦截器透明增强目标方法

结语

通过本文实现的调用链案例,我们揭示了AOP的核心工作原理:

  1. 洋葱模型:拦截器按顺序层层包裹目标方法
  2. 递归驱动:通过索引状态控制链式调用
  3. 双栈协同:JVM栈帧与自定义状态协同工作

这种设计在Spring AOP、Dubbo过滤器链等框架中广泛应用,其原理对于编写高性能拦截器和排查调用链问题至关重要。

完整代码

java 复制代码
package com.dwl.call_chain;

import jakarta.annotation.Nonnull;
import lombok.Data;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;

/**
 * @ClassName CallChainCase
 * @Description AOP调用链实现原理的深入演示,展示拦截器链的执行机制
 * @Version 1.0.0
 * @Date 2025
 * @Author By Dwl
 * <p>
 * 核心原理:递归+责任链模式实现的方法调用栈
 * 设计要点:
 * 1. "洋葱模型"结构:每个拦截器包裹下一个拦截器或目标方法
 * 2. 递归推进机制:通过索引计数器控制执行流程
 * 3. 方法调用栈:JVM调用栈+自定义调用链状态的协同工作
 */
@Slf4j
public class CallChainCase {

    /**
     * 目标Bean类,包含需要被拦截的业务方法
     * 设计说明:模拟实际的业务对象,无拦截器相关逻辑
     */
    static class CallChainBeanA {
        public void foo() {
            log.info("[CallChainBeanA] 执行了foo()方法(实际业务逻辑)");
        }
    }

    /**
     * 拦截器A实现(环绕通知)
     * 执行逻辑:
     * 1. 前置增强
     * 2. 调用proceed()推进调用链
     * 3. 后置增强
     * <p>
     * 方法栈深度原理:
     * ┌───────────────────┐
     * │  CallChainAdviceA │<─── 最外层栈帧(最先入栈)
     * ├───────────────────┤
     * │  CallChainAdviceB │
     * ├───────────────────┤
     * │  目标方法foo()    │
     * └───────────────────┘
     */
    static class CallChainAdviceA implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            log.info("╭── [AdviceA] 前置执行 (堆栈深度: {})", Thread.currentThread().getStackTrace().length);
            log.debug("│   [AdviceA] 开始构建调用栈帧");

            // JVM在此处压入新的栈帧(invocation.proceed)
            Object result = invocation.proceed(); // 关键调用点:推进调用链

            log.debug("│   [AdviceA] 栈帧返回,恢复执行");
            log.info("╰── [AdviceA] 后置执行 (结果: {})", result);
            return result;
        }
    }

    /**
     * 拦截器B实现(环绕通知)
     * 执行顺序说明:
     * 1. 当AdviceA调用proceed()时进入此处
     * 2. AdviceB是中间层拦截器
     */
    static class CallChainAdviceB implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            log.info("│   ╭── [AdviceB] 前置执行 (堆栈深度: {})", Thread.currentThread().getStackTrace().length);
            log.debug("│   │   [AdviceB] 创建中间层栈帧");

            // 继续推进调用链(可能指向下一个拦截器或目标方法)
            Object result = invocation.proceed();

            log.debug("│   │   [AdviceB] 中间层栈帧返回");
            log.info("│   ╰── [AdviceB] 后置执行");
            return result;
        }
    }

    /**
     * 自定义方法调用实现(核心引擎)
     * 功能:管理拦截器链的执行顺序和状态
     * <p>
     * 设计原理:
     * 1. 状态跟踪:currentIndex记录当前执行位置
     * 2. 递归终止条件:当所有拦截器执行完毕后调用目标方法
     * 3. 链式推进:递归调用interceptor.invoke(this)
     * <p>
     * 工作流程伪代码:
     * procedure proceed():
     * if 所有拦截器已执行:
     * 执行目标方法 // 递归基
     * else:
     * 获取当前拦截器
     * 索引位置+1
     * 执行拦截器.invoke(this) // 递归步骤
     */
    @Data
    static class MyMethodInvocation implements MethodInvocation {
        private Object target;          // 目标对象实例
        private Method method;          // 目标方法对象
        private Object[] args;          // 方法参数
        private List<MethodInterceptor> interceptorList; // 拦截器链
        private int currentIndex = 0;   // 当前执行位置(核心状态跟踪器)

        public MyMethodInvocation(Object target, Method method, Object[] args,
                                  List<MethodInterceptor> interceptorList) {
            this.target = target;
            this.method = method;
            this.args = args;
            this.interceptorList = interceptorList;
            log.debug("▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄");
            log.debug("▌ 创建调用链对象 [方法: {}]", method.getName());
            log.debug("▌ 目标对象: {}", target.getClass().getSimpleName());
            log.debug("▌ 拦截器数量: {}", interceptorList.size());
            log.debug("▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀");
        }

        @Override
        @NonNull
        public Method getMethod() {
            return this.method;
        }

        @Override
        @Nonnull
        public Object[] getArguments() {
            return args;
        }

        /**
         * 核心方法:推进调用链执行
         * 递归流程详解:
         * 1. 当currentIndex = N (拦截器数量)时达到递归基
         * 2. 每次proceed()调用会使currentIndex+1
         * 3. 递归深度 = 拦截器数量 + 1 (目标方法)
         * <p>
         * 递归调用栈图示:
         * ┌─────────────────┐
         * │ proceed()        │ currentIndex=0 → AdviceA
         * ├─────────────────┤
         * │ proceed()        │ currentIndex=1 → AdviceB
         * ├─────────────────┤
         * │ proceed()        │ currentIndex=2 → 目标方法
         * └─────────────────┘
         */
        @Override
        public Object proceed() throws Throwable {
            log.debug("├─ 推进调用链 [位置: {}/{}]", currentIndex, interceptorList.size());

            // 递归基:所有拦截器已执行完毕,执行目标方法
            if (currentIndex >= interceptorList.size()) {
                log.info("│   ╭── 执行目标方法 [调用栈深度: {}]", Thread.currentThread().getStackTrace().length);
                log.debug("│   │   ▌ 反射调用: {}.{}()",
                        target.getClass().getSimpleName(), method.getName());

                long startTime = System.nanoTime();
                Object result = invokeTargetMethod();  // 实际的方法调用点
                long duration = System.nanoTime() - startTime;

                log.debug("│   │   ▌ 执行时间: {} ns", duration);
                log.info("│   ╰── 目标方法完成 [结果: {}]", result);
                return result;
            }

            // 获取当前拦截器
            MethodInterceptor interceptor = interceptorList.get(currentIndex);
            log.debug("│   ├─ 获取拦截器 [{}]: {}", currentIndex,
                    interceptor.getClass().getSimpleName());

            // 状态推进:索引+1(准备进入下一层递归)
            currentIndex++;

            // 关键递归步骤:调用拦截器的invoke方法(将自身作为参数传入)
            log.debug("│   │   ╭── 进入拦截器调用 [深度提升]");
            Object result = interceptor.invoke(this);
            log.debug("│   │   ╰── 拦截器返回 [恢复调用链状态]");

            return result;
        }

        /**
         * 执行目标方法(安全处理)
         * 设计要点:单独封装反射调用,处理异常情况
         */
        private Object invokeTargetMethod() throws Throwable {
            try {
                return method.invoke(target, args);
            } catch (InvocationTargetException e) {
                throw e.getTargetException();  // 解包原始异常
            } catch (Exception e) {
                throw new RuntimeException("调用目标方法失败", e);
            }
        }

        @Override
        public Object getThis() {
            return target;
        }

        @Override
        @Nonnull
        public AccessibleObject getStaticPart() {
            return method;
        }
    }

    public static void main(String[] args) throws Throwable {
        log.info("\n\n============== 调用链执行流程演示 ==============");
        log.info("             (递归+责任链模式实现)\n");

        // 创建目标对象实例
        CallChainBeanA beanA = new CallChainBeanA();
        log.debug("▶ 实例化目标对象: {}", beanA.getClass().getSimpleName());

        // 创建拦截器链(执行顺序:A → B → 目标方法)
        List<MethodInterceptor> interceptors = List.of(
                new CallChainAdviceA(),
                new CallChainAdviceB()
        );
        log.debug("▶ 创建拦截器链: [AdviceA, AdviceB]");

        // 获取目标方法对象(反射)
        Method fooMethod = beanA.getClass().getMethod("foo");
        log.debug("▶ 获取目标方法: {}", fooMethod);

        // 创建调用链入口
        log.info("\n[主程序] 创建方法调用入口点");
        MyMethodInvocation invocation = new MyMethodInvocation(
                beanA, fooMethod, new Object[0], interceptors
        );

        log.info("\n[主程序] 开始执行调用链(初始proceed调用)");
        invocation.proceed();
        log.info("\n[主程序] 调用链执行完成");
        log.info("\n============== 执行流程结束 ==============");
    }
}
相关推荐
Dcs12 分钟前
微软 Copilot 被“越狱”?安全研究员教你一招拿下“沙箱环境 Root 权限”!
java
℡余晖^1 小时前
每日面试题18:基本数据类型和引用数据类型的区别
java
hello 早上好1 小时前
消息顺序、消息重复问题
java·中间件
phltxy1 小时前
ArrayList与顺序表
java·算法
Doris_LMS2 小时前
保姆级别IDEA关联数据库方式、在IDEA中进行数据库的可视化操作(包含图解过程)
java·mysql·postgresql
衍生星球2 小时前
JSP 程序设计之 Web 技术基础
java·开发语言·jsp
Java编程乐园2 小时前
Java函数式编程之【Stream终止操作】【下】【三】【收集操作collect()与分组分区】【下游收集器】
java
yinyan13142 小时前
一起学springAI系列一:初体验
java·人工智能·ai
永卿0012 小时前
设计模式-责任链模式
java·设计模式·责任链模式