八股文小记 Servlet 过滤器-Spring MVC 拦截器-Spring AOP 拦截器区别
您对执行机制的洞察非常准确!让我们深入分析这三种组件的调用机制及其与 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. 没有过滤器了——终点:调 Servletservlet.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后处理");}
}
调用栈示例:
2. MVC 拦截器:隐式遍历调用
源码调用所有拦截器
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) {HandlerExecutionChain mappedHandler = getHandler(request); // 获取执行链// 1. 前置拦截if (!mappedHandler.applyPreHandle(request, response)) return; // 2. 执行业务ControllerModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());// 3. 后置拦截mappedHandler.applyPostHandle(request, response, mv);// 4. 渲染视图后触发afterCompletionmappedHandler.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);
}
二、执行流程全景解析
三、后置增强的逻辑
后置增强并非独立的拦截器节点,而是通过递归返回机制实现的:
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();}
}
执行顺序:
三、拦截器方法与 AOP 节点的关系
1. MVC 拦截器 vs AOP 拦截器
特性 | MVC 拦截器方法 | AOP 拦截节点 |
---|---|---|
执行位置 | preHandle /postHandle | MethodInterceptor.invoke() |
调用方式 | 遍历调用 | 链式递归调用 |
目标对象 | Controller 方法 | 任意 Spring Bean 方法 |
访问权限 | 只能访问 HTTP 请求/响应 | 能访问方法参数、返回值、目标对象 |
是否共享节点 | ❌ 独立于 AOP 链 | ✅ 是 AOP 链的一部分 |
2. 关键区别图示
graph TBsubgraph HTTP请求流程Filter[Servlet过滤器] --> Interceptor[MVC拦截器]Interceptor --> Dispatcher[DispatcherServlet]endsubgraph Spring处理流程Dispatcher --> Proxy[Controller代理对象]Proxy --> AOP[AOP拦截链]AOP --> Bean[原始Controller方法]endnote[重要:MVC拦截器在AOP代理之外执行]
四、自定义组件加入位置
1. Servlet 过滤器加入点
@Configuration
public class FilterConfig {@Beanpublic 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 {@Overridepublic void addInterceptors(InterceptorRegistry registry) {// ▶ 加入遍历列表,按注册顺序执行 ◀registry.addInterceptor(new AuthInterceptor()).order(1) // 可选排序.addPathPatterns("/api/**");}
}
五、执行流程全景图
六、技术栈分层架构
总结
-
调用机制本质:
- 过滤器:显式递归调用(必须调用
chain.doFilter()
) - MVC拦截器:隐式遍历调用(框架自动控制)
- AOP拦截器:链式递归调用(通过
proceed()
传递)
- 过滤器:显式递归调用(必须调用
-
与AOP节点的关系:
- MVC拦截器 不是 AOP链的一部分
- Controller方法的执行会进入AOP代理链
- MVC拦截器在AOP代理链 之前 执行
-
自定义组件加入:
- 过滤器:通过
FilterRegistrationBean
加入Servlet容器调用链 - MVC拦截器:通过
WebMvcConfigurer
加入Spring MVC遍历列表 - AOP拦截器:通过
@Aspect
加入Spring AOP代理链
- 过滤器:通过
理解这些差异对于构建高效、可维护的Web应用至关重要:
- 使用过滤器处理底层HTTP问题(编码/安全)
- 使用MVC拦截器处理请求生命周期(认证/日志)
- 使用AOP拦截器处理业务逻辑(事务/性能监控)
这种分层设计让每个组件都能在最适合的层级发挥作用,既保持职责分离,又能协同完成复杂请求处理!