Spring AOP 源码-通知链的执行与责任链模式

在说这节的重点前,我们回顾一下几个重要概念:

  • 切面:封装了横切关注点的模块,包含通知和切点
  • 通知:定义增强逻辑的代码,前置、后置、环绕等
  • 切点:定义哪些方法需要被增强的匹配规则

那自然引出我们这节的重点,通知链,通知链指的是当一个方法匹配多个通知的情况下,AOP将这些串成一个链来依次执行。那我们上文说的代理对象就是拦截目标方法然后来执行通知链。

Spring AOP的通知链执行逻辑高度依赖责任链模式,通过链式调用协调多个通知的执行顺序,确保灵活性和扩展性,那下面我们具体来看下是如何实现的。另外,责任链模式是一种设计模式,而通知链是具体的应用,大家别搞混了。

1. 责任链模式特点

责任链模式是一种设计模式,也就是允许多个处理者依次处理请求,处理者决定是否处理还是给下一位,这个名字就很贴切,很符合链条式,比较核心的优点是:

  • 能够解耦请求与处理:请求发送者无需知道处理者
  • 动态组合:链上的处理者可以动态修改
  • 顺序执行

那在AOP的通知链就完美契合责任链模式,每个通知 就是一个处理者目标方法的调用请求,Spring通过代理对象将方法调用分发到通知链,通知按顺序执行。

2. Spring AOP通知链的执行流程

整个流程的调用链如下:

客户端调用->代理对象->通知链(Advice1->Advice2->Advice3......->目标方法)->返回结果

3. 通知链的构建与执行

SpringAOP的通知链实现在下面几个核心源码类里:

  • ReflectiveMethodInvocation:管理通知链的执行,维护通知列表和调用状态
  • CglibAopProxy和JdkDynamicAopProxy:实现代理逻辑,触发通知链
  • DefaultAdvisorChainFactory:构建通知链

下面我们从这三个类入手来看一下这一块的内容:

3.1 通知链的构建

DefaultAdvisorChainFactory 的核心方法是getInterceptorsAndDynamicInterceptors

scss 复制代码
public class DefaultAdvisorChainFactory implements AdvisorChainFactory {
    @Override
    public List<Object> getInterceptorsAndDynamicInterceptors(
            AdvisedSupport config, Method method, Class<?> targetClass) {
        List<Object> interceptorList = new ArrayList<>(config.getAdvisors().length);
        Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
        Advisor[] advisors = config.getAdvisors();

        // 遍历所有 Advisor
        for (Advisor advisor : advisors) {
            if (advisor instanceof PointcutAdvisor) {
                PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
                // 检查切点是否匹配
                if (pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass) &&
                        pointcutAdvisor.getPointcut().getMethodMatcher().matches(method, actualClass)) {
                    // 获取通知并转换为拦截器
                    MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
                    interceptorList.addAll(Arrays.asList(interceptors));
                }
            }
        }
        return interceptorList;
    }
}

这里的关键是:首先通过AdvisedSupportAdvisor 遍历,然后通过PointcutAdvisor的ClassFilterMethodMatcher判断方法是否需要增强,然后就是拦截器转换最后就是按优先级排序。

3.2 通知链的执行

ReflectiveMethodInvocation 的核心方法为proceed

kotlin 复制代码
public class ReflectiveMethodInvocation implements MethodInvocation {
    protected final Object proxy;
    protected final Object target;
    protected final Method method;
    protected Object[] arguments;
    private final List<?> interceptorsAndDynamicMethodMatchers;
    private int currentInterceptorIndex = -1;

    public ReflectiveMethodInvocation(
            Object proxy, Object target, Method method, Object[] arguments,
            Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {
        this.proxy = proxy;
        this.target = target;
        this.method = method;
        this.arguments = arguments;
        this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
    }

    @Override
    public Object proceed() throws Throwable {
        // 如果所有拦截器执行完毕,调用目标方法
        if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
            return invokeJoinpoint();
        }

        // 获取下一个拦截器
        Object interceptorOrInterceptionAdvice =
                this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
        if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
            InterceptorAndDynamicMethodMatcher dm =
                    (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
            if (dm.methodMatcher.matches(this.method, this.target.getClass(), this.arguments)) {
                return dm.interceptor.invoke(this);
            } else {
                return proceed(); // 跳过不匹配的拦截器
            }
        } else {
            // 执行拦截器逻辑
            return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
        }
    }

    protected Object invokeJoinpoint() throws Throwable {
        return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
    }
}

关键点是:

  • 链式调用:proceed方法通过currentInterceptorIndex维护当前执行的拦截器的位置,递归调用形成链式执行
  • 拦截器执行:每个拦截器(MethodInterceptor)通过invoke方法执行逻辑,并决定是否调用proceed继续链的执行
  • 目标方法调用:当所有拦截器执行完毕,invokeJoinpoint通过反射调用目标方法
  • 动态匹配:支持动态切点匹配,在运行时检查方法是否匹配

3.3 代理对象的拦截逻辑

这个我们在上篇文章说过,核心方法就是intercept:

typescript 复制代码
private static class DynamicAdvisedInterceptor implements MethodInterceptor {
    private final AdvisedSupport advised;

    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        // 获取通知链
        List<Object> chain = advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
        Object retVal;
        if (chain.isEmpty()) {
            // 无通知,直接调用目标方法
            retVal = methodProxy.invoke(target, args);
        } else {
            // 创建 MethodInvocation,触发通知链
            MethodInvocation invocation = new ReflectiveMethodInvocation(
                    proxy, target, method, args, targetClass, chain);
            retVal = invocation.proceed();
        }
        return retVal;
    }
}

关键点是:

  • 通知链获取:通过AdvisedSupport获取适用于当前方法的通知链
  • MethodInvocation创建:封装代理对象、目标对象、方法、参数和通知链
  • 链式调用:调用ReflectiveMethodInvocation.proceed触发通知链的执行

另外,Spring AOP默认按以下顺序来执行通知的:

@Around(前置)→ @Before → 目标方法 → @After → @Around(后置)

4. 总结

在整个源码中,有一些关键的优化,例如使用缓存、反射、拦截器统一拦截。那这篇文章主要有两个关键点,一个是通知链的构建还有一个链式执行

相关推荐
资深前端之路几秒前
react 面试题 react 有什么特点?
前端·react.js·面试·前端框架
sibylyue3 分钟前
Guava中常用的工具类
java·guava
奔跑吧邓邓子7 分钟前
【Java实战㉞】从0到1:Spring Boot Web开发与接口设计实战
java·spring boot·实战·web开发·接口设计
拉不动的猪13 分钟前
回顾vue中的Props与Attrs
前端·javascript·面试
专注API从业者16 分钟前
Python/Java 代码示例:手把手教程调用 1688 API 获取商品详情实时数据
java·linux·数据库·python
奔跑吧邓邓子37 分钟前
【Java实战㉝】Spring Boot实战:从入门到自动配置的进阶之路
java·spring boot·实战·自动配置
ONLYOFFICE38 分钟前
【技术教程】如何将ONLYOFFICE文档集成到使用Spring Boot框架编写的Java Web应用程序中
java·spring boot·编辑器
叫我阿柒啊1 小时前
Java全栈开发工程师的实战面试经历:从基础到微服务
java·微服务·typescript·vue·springboot·前端开发·后端开发
cyforkk1 小时前
Spring 异常处理器:从混乱到有序,优雅处理所有异常
java·后端·spring·mvc
生擒小朵拉1 小时前
STM32添加库函数
java·javascript·stm32