SpringBoot请求处理全链路:从Filter到Controller的完整旅程
2026/7/15 1:28:51 网站建设 项目流程

1. SpringBoot请求处理全链路概述

当你用浏览器访问一个SpringBoot应用的URL时,这个请求会经历一场奇妙的"旅行"。想象一下快递包裹从发货到签收的过程:包裹要经过分拣中心、运输车、配送站等多个环节,每个环节都有特定的处理逻辑。SpringBoot处理请求的流程也类似,从Tomcat容器接收请求开始,到最终返回响应,中间会经过过滤器、拦截器、参数绑定、控制器方法执行等多个关键节点。

我刚开始用SpringBoot时,经常搞不清楚过滤器和拦截器的区别,也不明白为什么有些异常能被@ControllerAdvice捕获而有些不能。后来通过阅读源码和实际踩坑,终于理清了整个流程。下面我就用最直白的语言,带你走一遍这个完整链路。

2. 请求处理全流程详解

2.1 Tomcat容器接收请求

当请求到达服务器时,Tomcat作为Servlet容器最先接手。它会把HTTP请求解析成HttpServletRequest对象,然后创建对应的HttpServletResponse对象。这个过程就像快递公司收到包裹后,先检查外包装是否完好,然后登记发货人和收货人信息。

Tomcat会维护一个线程池来处理并发请求,每个请求都会分配一个独立线程。这里有个性能优化点:合理配置Tomcat的线程池参数(比如maxThreads、acceptCount等)可以显著提高并发处理能力。我在实际项目中就遇到过因为线程池配置不当导致的请求堆积问题。

2.2 Filter过滤器链处理

Tomcat接下来会把请求交给Filter链处理。Filter是JavaEE标准,不依赖Spring,主要用于处理通用的横切关注点,比如:

  • 字符编码设置
  • 跨域请求处理
  • XSS防护
  • 权限校验
  • 请求日志记录

Filter的执行顺序由@Order注解或FilterRegistrationBean的order属性决定,数值越小优先级越高。常见的坑是忘记调用chain.doFilter()导致请求中断,我就犯过这个错误,调试了半天才发现问题。

// 典型Filter实现示例 public class LogFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { long start = System.currentTimeMillis(); // 前置处理(相当于快递分拣) System.out.println("请求开始:" + ((HttpServletRequest)request).getRequestURI()); chain.doFilter(request, response); // 放行到下一个Filter // 后置处理(相当于快递签收后的回执) System.out.println("请求耗时:" + (System.currentTimeMillis()-start) + "ms"); } }

2.3 DispatcherServlet分发请求

经过所有Filter后,请求来到Spring的核心入口DispatcherServlet。它就像公司的前台,负责把不同类型的请求分发给对应的部门(Controller)。DispatcherServlet的工作流程:

  1. 根据请求URL查找对应的Handler(控制器方法)
  2. 获取适合的HandlerAdapter(默认是RequestMappingHandlerAdapter)
  3. 执行拦截器链的preHandle方法
  4. 真正调用控制器方法
  5. 处理返回结果(JSON、视图等)
  6. 执行拦截器链的postHandle方法
  7. 处理异常(如果有)
  8. 执行拦截器链的afterCompletion方法

2.4 Interceptor拦截器链

Interceptor是SpringMVC的概念,比Filter更接近业务逻辑。典型应用场景包括:

  • 登录状态验证
  • 权限检查
  • 接口耗时统计
  • 防重复提交

拦截器有三个关键方法:

public class AuthInterceptor implements HandlerInterceptor { // 控制器方法执行前调用 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { // 验证token逻辑 return true; // 返回false会中断请求 } // 控制器方法执行后调用(异常时不执行) @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { // 可以修改返回的ModelAndView } // 请求完成后调用(始终执行) @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { // 资源清理等收尾工作 } }

2.5 参数绑定与方法执行

Spring会通过HandlerMethodArgumentResolver机制将请求参数绑定到方法参数上,包括:

  • @RequestParam:查询参数
  • @PathVariable:路径变量
  • @RequestBody:JSON/XML请求体
  • @ModelAttribute:表单数据
  • @RequestHeader:请求头

这里常见的坑是参数类型转换失败,比如前端传"abc"到Integer参数。可以通过自定义Converter或实现WebMvcConfigurer#addFormatters来扩展类型转换。

2.6 返回值处理

控制器方法执行完后,返回值会通过HandlerMethodReturnValueHandler处理:

  • @ResponseBody:使用HttpMessageConverter转换(如Jackson)
  • ModelAndView:视图渲染
  • String:视图名
  • void:根据请求URL自动找视图

我曾经遇到一个坑:返回的JSON字段名突然变成了驼峰转下划线,后来发现是配置了错误的Jackson PropertyNamingStrategy。

2.7 异常处理

Spring的异常处理流程:

  1. 先找@ExceptionHandler(当前Controller内)
  2. 再找@ControllerAdvice中的@ExceptionHandler
  3. 最后用默认的BasicErrorController

需要注意的是:

  • Filter中抛出的异常不会被@ControllerAdvice捕获
  • 拦截器afterCompletion抛出的异常只会记录日志
  • 想要全局处理Filter异常可以自定义ErrorController

3. 关键组件对比与最佳实践

3.1 Filter vs Interceptor

特性FilterInterceptor
作用范围Servlet容器级别SpringMVC级别
依赖Servlet APISpring框架
执行顺序最先执行在DispatcherServlet之后
获取Bean不能直接注入可以自动注入
使用场景通用预处理/后处理业务相关处理

实际项目中,我通常这样分工:

  • Filter处理:跨域、编码、XSS防护、全局日志
  • Interceptor处理:权限校验、接口限流、业务日志

3.2 常见问题解决方案

全局异常处理失效问题

  1. Filter异常:在第一个Filter中try-catch,或者实现ErrorController
  2. 拦截器afterCompletion异常:只能记录日志,无法阻止响应返回
  3. 多个@ControllerAdvice:用@Order控制顺序

性能优化建议

  1. 在Filter层做请求限流
  2. 使用@ControllerAdvice统一包装响应
  3. 复杂参数绑定实现自定义Converter
  4. 静态资源映射不要走SpringMVC流程

调试技巧

  1. 在application.properties中添加:
logging.level.org.springframework.web=DEBUG logging.level.org.springframework.boot.autoconfigure=DEBUG
  1. 使用SpringBoot Actuator的httptrace端点
  2. 自定义Filter和Interceptor时添加详细日志

4. 实战:自定义全链路日志

最后分享一个我在生产环境使用的全链路日志方案:

// 1. 定义TraceID过滤器 public class TraceFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { String traceId = UUID.randomUUID().toString(); MDC.put("traceId", traceId); // 用于日志追踪 ((HttpServletResponse)response).setHeader("X-Trace-ID", traceId); chain.doFilter(request, response); MDC.remove("traceId"); } } // 2. 定义日志拦截器 public class LogInterceptor implements HandlerInterceptor { private static final Logger log = LoggerFactory.getLogger(LogInterceptor.class); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { log.info("请求开始 {} {}", request.getMethod(), request.getRequestURI()); request.setAttribute("startTime", System.currentTimeMillis()); return true; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { long cost = System.currentTimeMillis() - (long)request.getAttribute("startTime"); log.info("请求完成 状态码:{} 耗时:{}ms", response.getStatus(), cost); } } // 3. 注册组件 @Configuration public class WebConfig implements WebMvcConfigurer { @Bean public FilterRegistrationBean<TraceFilter> traceFilter() { FilterRegistrationBean<TraceFilter> bean = new FilterRegistrationBean<>(); bean.setFilter(new TraceFilter()); bean.setOrder(Ordered.HIGHEST_PRECEDENCE); return bean; } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LogInterceptor()) .addPathPatterns("/**"); } }

这个方案可以清晰看到每个请求的完整生命周期,配合ELK等日志系统能快速定位问题。特别是在微服务架构下,通过TraceID可以串联起跨服务的调用链。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询