八股文小记 Servlet 过滤器-Spring MVC 拦截器-Spring AOP 拦截器区别

发布于:2025-08-17 ⋅ 阅读:(16) ⋅ 点赞:(0)

您对执行机制的洞察非常准确!让我们深入分析这三种组件的调用机制及其与 AOP 节点的关系:

一、执行机制的本质区别

组件 调用机制 实现原理
Servlet 过滤器 递归调用 通过 FilterChain.doFilter() 显式递归调用下一个节点
Spring MVC 拦截器 遍历调用 HandlerExecutionChain 内部维护列表,顺序遍历执行
Spring AOP 拦截器 链式递归调用 通过 MethodInvocation.proceed() 递归调用下一个拦截器,目标方法作为终点节点

二、详细机制解析

1. Servlet 过滤器:显式递归调用

源码调用,通过递归调用

public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
        if (Globals.IS_SECURITY_ENABLED) {
            ServletRequest req = request;
            ServletResponse res = response;

            try {
                AccessController.doPrivileged(() -> {
                    this.internalDoFilter(req, res); //调用过滤器链
                    return null;
                });
            } catch (PrivilegedActionException var7) {
                Exception e = var7.getException();
                if (e instanceof ServletException) {
                    throw (ServletException)e;
                }

                if (e instanceof IOException) {
                    throw (IOException)e;
                }

                if (e instanceof RuntimeException) {
                    throw (RuntimeException)e;
                }

                throw new ServletException(e.getMessage(), e);
            }
        } else {
            this.internalDoFilter(request, response);
        }

    }

private void internalDoFilter(ServletRequest request, ServletResponse response)
        throws IOException, ServletException {

    // 1. 还有过滤器就继续执行
    if (pos < n) {
        ApplicationFilterConfig filterConfig = filters[pos++];
        Filter filter = filterConfig.getFilter();
        filter.doFilter(request, response, this);  // 把 this(链本身)传回去,提供给后续过滤器使用,每次调用就会到这个过滤器链。
        return;
    }

    // 2. 没有过滤器了——终点:调 Servlet
    servlet.service(request, response);
}
public class MyFilter implements Filter {
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        // 前置处理 (递归入口)
        System.out.println("Filter前处理");
        
        // ▶ 显式递归调用:必须手动触发下一个节点 ◀
        chain.doFilter(request, response);
        
        // 后置处理 (递归返回)
        System.out.println("Filter后处理");
    }
}

调用栈示例

chain.doFilter
chain.doFilter
chain.doFilter
后处理
后处理
返回
Servlet.service
2. MVC 拦截器:隐式遍历调用

源码调用所有拦截器

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) {
    HandlerExecutionChain mappedHandler = getHandler(request); // 获取执行链
    // 1. 前置拦截
    if (!mappedHandler.applyPreHandle(request, response)) return; 
    // 2. 执行业务Controller
    ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    // 3. 后置拦截
    mappedHandler.applyPostHandle(request, response, mv);
    // 4. 渲染视图后触发afterCompletion
    mappedHandler.triggerAfterCompletion(request, response, null);
}
public class HandlerExecutionChain {
    private final List<HandlerInterceptor> interceptors = new ArrayList<>();
    
    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) {
        // ▶ 顺序遍历执行 preHandle ◀
        for (int i = 0; i < this.interceptors.size(); i++) {
            if (!interceptors.get(i).preHandle(request, response, this.handler)) {
                return false; // 中断
            }
        }
        return true;
    }
    
    void applyPostHandle(HttpServletRequest request, HttpServletResponse response, 
                        ModelAndView mv) throws Exception {
        // ▶ 逆序遍历执行 postHandle ◀
        for (int i = this.interceptors.size() - 1; i >= 0; i--) {
            interceptors.get(i).postHandle(request, response, this.handler, mv);
        }
    }
}
AOP增强,本质上也是拦截器MethodInterceptor
一、核心矛盾解析:为什么最后一个节点直接调用业务方法?

源码调用,每次调用将该拦截器链传入进去。ReflectiveMethodInvocation源码,外部使用这个出发拦截器链。

public Object proceed() throws Throwable {
    // 检查是否到达链末端
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return invokeJoinpoint(); // 执行业务方法
    }
    
    // 获取下一个拦截器
    Object interceptorOrInterceptionAdvice =
        this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    
    // 递归调用下一个拦截器 ▼▼▼ 核心递归点 ▼▼▼
    return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
二、执行流程全景解析
proceed()方法 拦截器1 拦截器2 业务方法 调用invoke(this) 执行前置逻辑 调用proceed() 调用invoke(this) 执行前置逻辑 调用proceed() 执行原始业务方法 返回结果 返回结果 执行后置逻辑 返回结果 执行后置逻辑 返回结果 proceed()方法 拦截器1 拦截器2 业务方法

三、后置增强的逻辑

后置增强并非独立的拦截器节点,而是通过递归返回机制实现的:

1. 拦截器的标准结构
public class MyInterceptor implements MethodInterceptor {
    public Object invoke(MethodInvocation mi) throws Throwable {
        // 前置逻辑
        System.out.println("Before business method");
        
        // 关键:递归调用链
        Object result = mi.proceed();
        
        // 后置逻辑
        System.out.println("After business method");
        return result;
    }
}
2. 执行栈展开过程
// 伪代码表示执行栈
Stack:
1. MyInterceptor.invoke() 
   -> 调用 mi.proceed()
   
2. ReflectiveMethodInvocation.proceed() 
   -> 调用下一个拦截器
   
... 递归直到最后一个 ...

N. ReflectiveMethodInvocation.proceed()
   -> 执行原始方法
   -> 返回结果
   
// 栈开始展开
N-1. 上一个拦截器收到结果
     -> 执行后置逻辑
     -> 返回结果
     
... 递归返回 ...

1. 第一个拦截器收到结果
   -> 执行后置逻辑
   -> 返回最终结果

四、源码验证:Spring 内置拦截器实现

1. 后置返回通知 (AfterReturningAdviceInterceptor)
public Object invoke(MethodInvocation mi) throws Throwable {
    // 前置:无操作
    
    // 关键:先执行后续链(包含业务方法)
    Object retVal = mi.proceed();
    
    // 后置:在业务方法返回后执行
    this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
    return retVal;
}
2. 最终通知 (AspectJAfterAdvice)
public Object invoke(MethodInvocation mi) throws Throwable {
    try {
        // 关键:先执行后续链
        return mi.proceed();
    } finally {
        // 后置:在finally块中确保执行
        invokeAdviceMethod();
    }
}

执行顺序

遍历preHandle
Interceptor1.pre
Interceptor2.pre
Interceptor3.pre
Controller
逆序postHandle
Interceptor3.post
Interceptor2.post
Interceptor1.post

三、拦截器方法与 AOP 节点的关系

1. MVC 拦截器 vs AOP 拦截器
特性 MVC 拦截器方法 AOP 拦截节点
执行位置 preHandle/postHandle MethodInterceptor.invoke()
调用方式 遍历调用 链式递归调用
目标对象 Controller 方法 任意 Spring Bean 方法
访问权限 只能访问 HTTP 请求/响应 能访问方法参数、返回值、目标对象
是否共享节点 ❌ 独立于 AOP 链 ✅ 是 AOP 链的一部分
2. 关键区别图示
graph TB
    subgraph HTTP请求流程
        Filter[Servlet过滤器] --> Interceptor[MVC拦截器]
        Interceptor --> Dispatcher[DispatcherServlet]
    end
    
    subgraph Spring处理流程
        Dispatcher --> Proxy[Controller代理对象]
        Proxy --> AOP[AOP拦截链]
        AOP --> Bean[原始Controller方法]
    end
    
    note[重要:MVC拦截器在AOP代理之外执行]

四、自定义组件加入位置

1. Servlet 过滤器加入点
@Configuration
public class FilterConfig {
    @Bean
    public FilterRegistrationBean<MyFilter> myFilter() {
        FilterRegistrationBean<MyFilter> bean = new FilterRegistrationBean<>();
        bean.setFilter(new MyFilter());
        bean.setOrder(1); // ▶ 关键:通过order控制递归顺序 ◀
        bean.addUrlPatterns("/*");
        return bean;
    }
}
2. MVC 拦截器加入点
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // ▶ 加入遍历列表,按注册顺序执行 ◀
        registry.addInterceptor(new AuthInterceptor())
                .order(1) // 可选排序
                .addPathPatterns("/api/**");
    }
}

五、执行流程全景图

Client Filter1 Filter2 DispatcherServlet MVC Interceptor AOP Interceptor Controller HTTP Request 前置处理 chain.doFilter() 前置处理 chain.doFilter() preHandle() true/false 进入代理 拦截器1 拦截器2 目标方法 结果 拦截器后处理 返回 postHandle() 直接返回 alt [继续执行] [中断] 后置处理 返回 后置处理 HTTP Response Client Filter1 Filter2 DispatcherServlet MVC Interceptor AOP Interceptor Controller

六、技术栈分层架构

HTTP请求
Servlet容器层
Servlet过滤器
Spring Web层
MVC拦截器
Controller代理
Spring AOP层
业务拦截器
原始Bean方法

总结

  1. 调用机制本质

    • 过滤器:显式递归调用(必须调用 chain.doFilter()
    • MVC拦截器:隐式遍历调用(框架自动控制)
    • AOP拦截器:链式递归调用(通过 proceed() 传递)
  2. 与AOP节点的关系

    • MVC拦截器 不是 AOP链的一部分
    • Controller方法的执行会进入AOP代理链
    • MVC拦截器在AOP代理链 之前 执行
  3. 自定义组件加入

    • 过滤器:通过 FilterRegistrationBean 加入Servlet容器调用链
    • MVC拦截器:通过 WebMvcConfigurer 加入Spring MVC遍历列表
    • AOP拦截器:通过 @Aspect 加入Spring AOP代理链

理解这些差异对于构建高效、可维护的Web应用至关重要:

  • 使用过滤器处理底层HTTP问题(编码/安全)
  • 使用MVC拦截器处理请求生命周期(认证/日志)
  • 使用AOP拦截器处理业务逻辑(事务/性能监控)

这种分层设计让每个组件都能在最适合的层级发挥作用,既保持职责分离,又能协同完成复杂请求处理!