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/11/10 19:20:21 阅读更多 →
context-mode 上下文管理:从 Neovim 插件到 AI 对话的通用原理

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

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

2026/11/11 5:21:34 阅读更多 →
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/11/10 11:26:29 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年&#xff0c;我大部分时间都在跟浏览器标签页打交道&#xff0c;直到第一次把桌面应用跑起来&#xff0c;才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词&#xff0c;最开始是在某段时间频繁出现在同事的讨论里&#xff0c;一个用HTML…

2026/11/12 5:00:00 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介&#xff1a;亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档&#xff0c;主要面向智能锁安装人员、终端用户及售后维护者&#xff0c;帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件&#xff0c;大小4.37MB&#xff0c;内容结构清晰&#xff…

2026/11/12 5:13:20 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统&#xff0c;整车要求空载跑到8m/s&#xff0c;电机转速已经拉到额定点&#xff0c;驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令&#xff0c;转速纹丝不动&#xff0c;三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/11/12 5:13:20 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年&#xff0c;用它写过嵌入式驱动、做过游戏服务端&#xff0c;也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词&#xff0c;我其实挺感慨的——这门语言的难点从来不是语法多&#xff0c;而是知识点之间的耦合太深…

2026/11/11 17:16:37 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介&#xff1a;亚太天能T109滑盖智能锁产品说明书以PDF格式发布&#xff0c;适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开&#xff0c;先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能&#xff0c;再给出完整安装步骤&…

2026/11/11 17:16:37 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历&#xff1a;翻开源码注释、论文摘要或者一份专业报告&#xff0c;每个字都认识&#xff0c;连在一起却像天书。想找人聊聊&#xff0c;身边要么没人懂&#xff0c;要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型&#xff0c;几秒钟之后&a…

2026/11/12 5:13:20 阅读更多 →

日新闻

周新闻

月新闻