Spring中过滤器和拦截器的区别是什么?
两个东西都是横切用的写法也像实现一个接口把逻辑塞进去就能在 Controller 前后插一脚。区别在站在哪一层。Filter来自 Servlet 规范是 Servlet 容器Tomcat调用的Interceptor是 Spring MVC 的东西由DispatcherServlet调用。所以 Filter 在DispatcherServlet外面Interceptor 在DispatcherServlet里面后面那些差别基本都能落到这个位置上。请求进来的那条链我们先把请求进来之后的链路画出来FilterpublicinterfaceFilter{defaultvoidinit(FilterConfigfilterConfig)throwsServletException{}voiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException;defaultvoiddestroy(){}}三个方法里只有doFilter是必须实现的。init和destroy分别在这个 Filter 第一次用到和容器关闭时各调一次。doFilter的放行方式就是调chain.doFilter(request, response)不调就相当于拦住了请求不会往下走。下面这几行代码是 Filter 最常见的一个形态ComponentpublicclassCostFilterimplementsFilter{OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{longstartSystem.currentTimeMillis();try{chain.doFilter(request,response);}finally{System.out.println(cost (System.currentTimeMillis()-start)ms);}}}耗时统计写在chain.doFilter前后因为这一句里面才是后面整条链包括DispatcherServlet。拿到的ServletRequest是容器给的原始对象getRequestURI()、getHeader()这些都得自己 cast 成HttpServletRequest。想往里加东西比如记一个 traceId 往下传只能自己包一层HttpServletRequestWrapperHttpServletRequestwrappernewHttpServletRequestWrapper((HttpServletRequest)request){OverridepublicStringgetHeader(Stringname){if(X-Trace-Id.equals(name)){returntraceId;}returnsuper.getHeader(name);}};chain.doFilter(wrapper,response);响应同理HttpServletResponseWrapper包一层再传下去。请求体也是这个套路ContentCachingRequestWrapper/ContentCachingResponseWrapper就是 Spring 给的现成 wrapper。这里提醒一句请求体只能在 Filter 里读一次。如果你的 Filter 里调了request.getInputStream()把 body 读出来打印后面RequestBody拿到的就是空字符串。要么用ContentCachingRequestWrapper包一层要么在 Filter 里别碰 body。jakarta.servlet还是javax.servlet看 Spring Boot 版本。Boot 3 跟着 Tomcat 10 换成了jakarta.*从旧项目拷 Filter 的时候这里必改。注册三种方式// 1. 直接标 ComponentSpring Boot 会把它注册到 /*ComponentpublicclassCostFilterimplementsFilter{...}// 2. FilterRegistrationBean能指定 urlPatterns、顺序、dispatcher typeBeanpublicFilterRegistrationBeanCostFiltercostFilter(){FilterRegistrationBeanCostFilterbeannewFilterRegistrationBean(newCostFilter());bean.addUrlPatterns(/api/*);bean.setOrder(1);returnbean;}// 3. 走 Servlet 原生的 WebFilter需要在启动类上加 ServletComponentScanWebFilter(urlPatterns/*)publicclassCostFilterimplementsFilter{AutowiredprivateUserServiceuserService;// 注入不进去见下面}第三种要注意WebFilter标注的 Filter 由 Servlet 容器new出来不是 Spring 容器里的 beanAutowired不生效。InterceptorpublicinterfaceHandlerInterceptor{defaultbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{returntrue;}defaultvoidpostHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,ModelAndViewmodelAndView)throwsException{}defaultvoidafterCompletion(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,Exceptionex)throwsException{}}三个方法的时机方法时机参数里有什么preHandleController 方法执行前handler是HandlerMethod返回false就不往下走了postHandleController 执行完、视图渲染前多了ModelAndView可以往里塞东西afterCompletion整个请求结束视图渲染完ex是这次请求抛出的异常没异常就是nullRequestMapping匹配到的方法在这里就是HandlerMethod能拿到 Controller 类和Method对象也能读它上面的注解。所以只拦带某个注解的接口这种需求只有拦截器能做publicclassAuthInterceptorimplementsHandlerInterceptor{OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{if(!(handlerinstanceofHandlerMethodhandlerMethod)){returntrue;// 静态资源这类不是 HandlerMethod直接放行}if(!handlerMethod.hasMethodAnnotation(RequireLogin.class)){returntrue;}Stringtokenrequest.getHeader(Authorization);if(tokennull){response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);returnfalse;}returntrue;}}注册拦截器不能标个注解就生效得往InterceptorRegistry里注册ConfigurationpublicclassWebConfigimplementsWebMvcConfigurer{OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){registry.addInterceptor(newAuthInterceptor()).addPathPatterns(/**).excludePathPatterns(/login,/error,/static/**).order(1);}}preHandle返回false之后请求直接返回postHandle不执行但已经通过preHandle的拦截器它们的afterCompletion还是会执行ex传null。这一点和 Filter 里不放行就整个断掉不一样资源释放写在afterCompletion里是安全的。对比FilterInterceptor所属Servlet 规范jakarta.servletSpring MVC谁调用Servlet 容器DispatcherServlet实现接口FilterHandlerInterceptor入口doFilter(req, resp, chain)一个方法preHandle/postHandle/afterCompletion放行方式chain.doFilter()不调就断preHandle返回true拦的范围容器收到的所有请求DispatcherServlet找得到 handler 的请求看得到 Controller 方法看不到能handler就是HandlerMethod包装 request / response能包一层再往下传不能只能直接往response里写抛异常的归宿容器错误页Boot 的/errorHandlerExceptionResolverControllerAdvice能接注册FilterRegistrationBean/Component/WebFilterWebMvcConfigurer#addInterceptors顺序Order、setOrder()数字小的先注册顺序或.order()方向按顺序进、按逆序出preHandle正序postHandle/afterCompletion逆序几个容易混的点静态资源归谁拦Component的 Filter 默认映射到/*容器收到的请求都会经过它包括那些压根不由 Spring MVC 处理的Druid 的StatViewServlet、H2 的控制台这些都是独立注册到容器上的 servlet只有 Filter 拦得到。拦截器这边addPathPatterns(/**)也会拦到静态资源因为静态资源在 Spring MVC 里也是一个 handlerResourceHttpRequestHandler。所以实际项目里写了/**之后一般都会补一句excludePathPatterns(/static/**, /favicon.ico)否则登录校验会拦到 CSS 上页面直接白板。异常去哪了DispatcherServlet.doDispatch里的try是从applyPreHandle就开始包的preHandle抛出来的异常会和 Controller 里抛的一样走到processDispatchResult→HandlerExceptionResolver→ControllerAdvice。所以拦截器里直接throw new BizException(...)是能拿到统一响应体的。Filter 就不行了。它跑在DispatcherServlet外面HandlerExceptionResolver根本没机会看到异常一路冒到容器最后交给 Boot 的错误处理页面/error返回的是 BasicErrorController 那个格式。想在 Filter 层也保持统一响应体就得自己在 Filter 里 catch 住然后手写 JSON或者干脆别在 Filter 里做校验。Filter 里能不能Autowired能但要看这个 Filter 是怎么来的。Component注册的 Filter 本身就是一个 Spring bean依赖注入、Value、ConfigurationProperties都正常。WebFilterServletComponentScan那条路不行Filter 是容器new的和 Spring 容器没关系。这就是DelegatingFilterProxy存在的原因它自己是个 Filter容器 new 出来的没关系它不需要依赖doFilter的时候按名字去 Spring 容器里找真正的 Filter 然后委托过去。filterfilter-namespringSecurityFilterChain/filter-namefilter-classorg.springframework.web.filter.DelegatingFilterProxy/filter-class/filterSpring Security 的一整条过滤器链就挂在这个名字底下。启动日志里那句Will secure any request with [...]列出来的那些SecurityFilterChain成员都是被这一层代理转进去的。顺序Filter 的顺序由Order或者FilterRegistrationBean.setOrder()决定数字小的先执行、后返回。拦截器的顺序由addInterceptors里注册的先后决定.order()也可以指定。preHandle正序执行、postHandle和afterCompletion逆序执行这一点和 Filter 的进去一圈、出来一圈是一致的可以照着最上面那张链路图对一遍。另外 Filter 一定在拦截器之前因为拦截器的调用方DispatcherServlet本身就是 Filter 链末端的一个 servlet。异步请求Controller 返回Callable、DeferredResult这类异步结果时DispatcherServlet会先把请求线程放掉postHandle和afterCompletion都不会在那一刻调用。afterCompletion会在异步结果处理完之后回调postHandle则整个跳过。如果我们依赖postHandle做清理异步接口上就会漏掉。要在请求线程被放掉的那一刻做点事用AsyncHandlerInterceptor它比HandlerInterceptor多一个afterConcurrentHandlingStarted回调。

相关新闻

Flutter鸿蒙全栈:vane中间件驱动Dart服务端适配实践

Flutter鸿蒙全栈:vane中间件驱动Dart服务端适配实践

Flutter 做客户端,Dart 写服务端,再把服务端进程直接塞进鸿蒙工程里跑起来——这三件事单独拆开都不算新鲜,但把 vane 这个中间件驱动的 Dart 服务端框架完整适配到鸿蒙环境,让一套代码既是 App 又是 API 服务,真正踩过…

2026/10/11 11:26:31 阅读更多 →
context-mode 上下文管理:从 Neovim 插件到 AI 对话的通用原理

context-mode 上下文管理:从 Neovim 插件到 AI 对话的通用原理

我是在一次翻一个将近 3000 行的旧配置文档时&#xff0c;才真正意识到 context-mode 这个关键词的分量。当时光标已经滚到文件底部&#xff0c;屏幕上全是密密麻麻的配置项&#xff0c;而我完全想不起来当前这一段到底属于哪个功能模块&#xff0c;只能反复按<C-u>往回翻…

2026/10/12 5:51:23 阅读更多 →
Python深度学习课堂行为图像识别项目:数据组织、模型选型与训练避坑指南

Python深度学习课堂行为图像识别项目:数据组织、模型选型与训练避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 18:05:06 阅读更多 →

最新新闻

ROS Melodic 离线安装方案:Ubuntu 18.04 下 rosdep 本地化与依赖处理

ROS Melodic 离线安装方案:Ubuntu 18.04 下 rosdep 本地化与依赖处理

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 1:04:15 阅读更多 →
多段图最短路径:动态规划状态设计与Python实现

多段图最短路径:动态规划状态设计与Python实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 1:04:15 阅读更多 →
双串锂电池主动均衡方案:MP2642GR-Z与STM32G070RB实战

双串锂电池主动均衡方案:MP2642GR-Z与STM32G070RB实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 1:04:15 阅读更多 →
RabbitMQ分布式事务实战:本地消息表实现最终一致性

RabbitMQ分布式事务实战:本地消息表实现最终一致性

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 1:04:15 阅读更多 →
基于MP2642GR-Z与STM32F410RB的两串电池主动均衡方案设计与实现

基于MP2642GR-Z与STM32F410RB的两串电池主动均衡方案设计与实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 1:04:15 阅读更多 →
基于MP2642GR-Z与MSP432的两串电芯被动均衡方案设计与实现

基于MP2642GR-Z与MSP432的两串电芯被动均衡方案设计与实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 1:03:14 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介&#xff1a;《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南&#xff0c;重点围绕V3与R1两款模型的能力差异与适用场景展开&#xff0c;帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件&#xff0c;压缩包大小约311KB&#xff0c;篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介&#xff1a;这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档&#xff0c;共收录两百余条可直接套用的润色指令&#xff0c;涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度&#xff0c;并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介&#xff1a;本资源是一份基于OpenCV的本科课程设计完整报告&#xff0c;面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生&#xff0c;解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介&#xff1a;基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码&#xff0c;面向计算机相关专业课程设计与期末大作业学生&#xff0c;以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程&#xff0c;…

2026/10/16 21:35:51 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程&#xff1a;键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化&#xff0c;十个新手有八个栽在"往输入框里填东西"这件事上&#xff1a;要么填不进去&#xff0c;要么填了一半&#xff0c;要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程&#xff1a;阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀&#xff1a;什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面&#xff0c;跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 21:54:38 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →