今天在deepseek上偶然问了这个问题,给出的描述很清楚,抄下来。
拦截器链执行核心
public class ReflectiveMethodInvocation implements MethodInvocation {private final Object target;private final Method method;private final Object[] args;private final List<MethodInterceptor> interceptors;// 关键:当前拦截器索引,记录执行位置private int currentInterceptorIndex = -1;public ReflectiveMethodInvocation(Object target, Method method, Object[] args, List<MethodInterceptor> interceptors) {this.target = target;this.method = method;this.args = args;this.interceptors = interceptors;}@Overridepublic Object proceed() throws Throwable {// 所有拦截器都执行完毕,执行原始目标方法if (currentInterceptorIndex == interceptors.size() - 1) {return method.invoke(target, args);}// 获取下一个拦截器并执行MethodInterceptor interceptor = interceptors.get(++currentInterceptorIndex);return interceptor.invoke(this); // 关键:递归调用链 } }
里面的invoke代码不难,类似这样:
@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {// 先执行前置逻辑 beforeAdvice.run();// 继续执行链return invocation.proceed();}
所以proceed差不多可以简化成这样:
@Overridepublic Object proceed() throws Throwable {// 所有拦截器都执行完毕,执行原始目标方法if (currentInterceptorIndex == interceptors.size() - 1) {return method.invoke(target, args);}// 获取下一个拦截器并执行MethodInterceptor interceptor = interceptors.get(++currentInterceptorIndex);return proceed();}
spring把controller里我们实现的method用一个结构体(class)描述出来,调用的method名字,参数,包括外面用aspect做的切面都塞到结构体里。叫做ReflectiveMethodInvocation。按我的习惯就叫execution。proceed()是一个递归函数,它总是执行下一个切面,除非没有切面了,再执行真正的用户方法。
注意method本体的调用是在else里的,一个切面如果进入另一个切面,它就不执行method了。所以只有咀内层(最末序)的切面有调用method的机会,如果它也没调用,那这个controller的方法就被绕过了。
没接触spring的时候,猜测切面应该就是钩子吧,其实是比钩子高级的钩子,重要的是它把方法调用描述成数据结构,然后再“手动”调用,这样对方法所处的执行点位的执行流和执行内容能全权控制。