news 2026/8/7 11:22:33

第27篇-拦截器与过滤器

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
第27篇-拦截器与过滤器

【Kotlin + Spring Boot 4 从零到架构师】第 27 篇:拦截器与过滤器

本系列定位:零基础入门,从 Kotlin 语法一路到 Spring Boot 4 高级架构(DDD + Modulith),适合 Java 开发者转型,也适合纯新手系统学习。


本篇你将学到

  • Filter(过滤器)与 Interceptor(拦截器)的区别
  • HandlerInterceptor的三个生命周期方法
  • OncePerRequestFilter的使用
  • 执行顺序与使用场景

学完本篇,你将能为 mini-shop 添加请求日志、耗时统计、访问控制等横切功能。


一、Filter vs Interceptor

1.1 执行位置

Spring MVC层

Servlet容器层

客户端请求

Filter (doFilter)

DispatcherServlet

Interceptor (preHandle)

Controller

Interceptor (postHandle)

Interceptor (afterCompletion)

响应返回客户端

1.2 核心区别

维度FilterInterceptor
所属Servlet 容器(Tomcat)Spring MVC
执行时机DispatcherServlet 前后Controller 前后
能否访问 Controller不能能(知道要执行哪个 Controller 方法)
能否访问 Spring Bean需要手动获取可以直接注入
使用场景编码、CORS、日志、安全权限校验、日志、性能监控
注册方式FilterRegistrationBeanWebMvcConfigurer

二、HandlerInterceptor

2.1 三个生命周期方法

interfaceHandlerInterceptor{// Controller 执行前funpreHandle(request,response,handler):Boolean// Controller 执行后、视图渲染前funpostHandle(request,response,handler,modelAndView)// 请求完成后(无论成功失败)funafterCompletion(request,response,handler,ex)}
方法执行时机返回值典型用途
preHandleController 之前true:继续;false:中断权限校验、限流
postHandleController 之后void修改 Model、添加 Header
afterCompletion全部完成后void清理资源、记录日志

2.2 实战示例:请求日志 + 耗时统计

packagecom.example.minishop.interceptorimportjakarta.servlet.http.HttpServletRequestimportjakarta.servlet.http.HttpServletResponseimportorg.slf4j.LoggerFactoryimportorg.springframework.stereotype.Componentimportorg.springframework.web.servlet.HandlerInterceptor@ComponentclassRequestLoggingInterceptor:HandlerInterceptor{privatevallog=LoggerFactory.getLogger(RequestLoggingInterceptor::class.java)privatevalstartTimeAttribute="requestStartTime"overridefunpreHandle(request:HttpServletRequest,response:HttpServletResponse,handler:Any):Boolean{valstartTime=System.currentTimeMillis()request.setAttribute(startTimeAttribute,startTime)log.info("→${request.method}${request.requestURI}from${request.remoteAddr}")returntrue// 继续}overridefunafterCompletion(request:HttpServletRequest,response:HttpServletResponse,handler:Any,ex:Exception?){valstartTime=request.getAttribute(startTimeAttribute)asLongvalduration=System.currentTimeMillis()-startTimevalstatus=response.statusvalexceptionInfo=if(ex!=null)" exception=${ex.message}"else""log.info("←${request.method}${request.requestURI}$status${duration}ms$exceptionInfo")}}

2.3 注册拦截器

packagecom.example.minishop.configimportcom.example.minishop.interceptor.RequestLoggingInterceptorimportorg.springframework.context.annotation.Configurationimportorg.springframework.web.servlet.config.annotation.InterceptorRegistryimportorg.springframework.web.servlet.config.annotation.WebMvcConfigurer@ConfigurationclassWebConfig(privatevalrequestLoggingInterceptor:RequestLoggingInterceptor):WebMvcConfigurer{overridefunaddInterceptors(registry:InterceptorRegistry){registry.addInterceptor(requestLoggingInterceptor).addPathPatterns("/api/**") // 拦截 /api/ 下所有请求 .excludePathPatterns("/api/health") // 排除健康检查端点 } }

2.4 实战示例:简单权限校验

@ComponentclassAuthInterceptor(// 拦截器可以注入 Spring BeanprivatevalauthService:AuthService):HandlerInterceptor{overridefunpreHandle(request:HttpServletRequest,response:HttpServletResponse,handler:Any):Boolean{valtoken=request.getHeader("Authorization")if(token.isNullOrBlank()){response.contentType="application/json;charset=UTF-8"response.status=HttpServletResponse.SC_UNAUTHORIZED response.writer.write("""{"code":401,"message":"未提供认证 Token"}""")returnfalse// 中断请求}valisValid=authService.validateToken(token)if(!isValid){response.contentType="application/json;charset=UTF-8"response.status=HttpServletResponse.SC_UNAUTHORIZED response.writer.write("""{"code":401,"message":"Token 无效或已过期"}""")returnfalse}returntrue// 放行}}

注册时指定拦截路径:

overridefunaddInterceptors(registry:InterceptorRegistry){registry.addInterceptor(authInterceptor).addPathPatterns("/api/**") .excludePathPatterns( "/api/auth/login", // 登录接口不拦截 "/api/auth/register", // 注册接口不拦截 "/api/health", // 健康检查不拦截 "/api/products/**" // 商品浏览不拦截(公开接口) ) }

三、Filter

3.1 OncePerRequestFilter

Spring 提供了OncePerRequestFilter,确保每个请求只执行一次(避免请求转发时重复执行):

packagecom.example.minishop.filterimportjakarta.servlet.FilterChainimportjakarta.servlet.http.HttpServletRequestimportjakarta.servlet.http.HttpServletResponseimportorg.slf4j.LoggerFactoryimportorg.springframework.stereotype.Componentimportorg.springframework.web.filter.OncePerRequestFilter@ComponentclassRequestIdFilter:OncePerRequestFilter(){privatevallog=LoggerFactory.getLogger(RequestIdFilter::class.java)overridefundoFilterInternal(request:HttpServletRequest,response:HttpServletResponse,filterChain:FilterChain){// 1. 从请求头获取或生成请求 IDvalrequestId=request.getHeader("X-Request-Id")?:generateRequestId()// 2. 设置到响应头中(方便客户端排查)response.setHeader("X-Request-Id",requestId)// 3. 存入请求属性(供后续 Interceptor / Controller 使用)request.setAttribute("requestId",requestId)try{// 4. 继续过滤器链filterChain.doFilter(request,response)}finally{log.debug("请求处理完成:requestId=$requestId")}}privatefungenerateRequestId():String{returnjava.util.UUID.randomUUID().toString().take(8)}}

3.2 Filter 的注册

Spring Boot 会自动注册带有@Component的 Filter。如果需要自定义 URL 匹配:

@ConfigurationclassFilterConfig{@BeanfunrequestIdFilterRegistration(filter:RequestIdFilter):FilterRegistrationBean<RequestIdFilter>{valregistration=FilterRegistrationBean(filter)registration.addUrlPatterns("/api/*") // 只拦截 /api/ 路径 registration.order = 1 // 执行顺序(数字越小越先执行) return registration } }

四、执行顺序总结

响应处理

请求处理

请求

Filter (按order升序)

Interceptor.preHandle

Controller

Interceptor.postHandle

Interceptor.afterCompletion

Filter (逆序)

响应

多过滤器场景

请求

Filter A (order=1)

Filter B (order=2)

Interceptor.preHandle

Controller

Interceptor.postHandle

Interceptor.afterCompletion

Filter B (逆序)

Filter A (逆序)

响应


五、选择指南

需求选择原因
CORS 处理Filter在最外层处理,不进入 Spring MVC
字符编码设置Filter最早的时机设置编码
请求日志Interceptor能拿到 Controller 信息
耗时统计Interceptor精确到 Controller 执行时间
权限/角色校验Interceptor能获取 Handler 信息
请求 ID 追踪Filter最外层生成,全链路可用
限流Filter 或 Interceptor都可以,看是否需要 Spring 上下文

本篇小结

知识点核心内容
FilterServlet 层,DispatcherServlet 之前/之后
InterceptorSpring MVC 层,Controller 之前/之后
preHandle返回 false 中断请求
postHandleController 之后,可修改 Model
afterCompletion最终清理,无论成功失败
OncePerRequestFilter确保每个请求只过滤一次
WebMvcConfigurer注册拦截器 + 配置路径
执行顺序Filter → Interceptor → Controller

下篇预告

第 28 篇:CORS 与文件上传

前端跨域请求被拦截怎么办?商品图片怎么上传到服务器?下一篇解决这两个常见的 Web 开发问题。


如果本篇内容对你有帮助,欢迎点赞收藏!有任何疑问,欢迎在评论区交流。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/7 11:21:17

C++高性能开发进阶:从CPU指令集到游戏引擎架构的实战指南

1. 项目概述&#xff1a;为什么从指令集到引擎是C开发者的必经之路 最近在社区里看到不少朋友在讨论C的学习路径&#xff0c;有的在纠结于语法细节&#xff0c;有的在刷各种“八股文”面试题&#xff0c;还有的在用C写一些控制台小游戏。这让我想起自己刚入行那会儿&#xff0c…

作者头像 李华
网站建设 2026/8/7 11:20:30

酷安UWP桌面版终极指南:在Windows上畅享完整酷安社区体验

酷安UWP桌面版终极指南&#xff1a;在Windows上畅享完整酷安社区体验 【免费下载链接】Coolapk-UWP 一个基于 UWP 平台的第三方酷安客户端 项目地址: https://gitcode.com/gh_mirrors/co/Coolapk-UWP 你是否厌倦了在小屏幕手机上刷酷安&#xff0c;渴望在电脑大屏幕上享…

作者头像 李华
网站建设 2026/8/7 11:05:29

数据中台的建设成本为什么总失控-花了几百万还没打通数据

# 数据中台的建设成本为什么总失控-花了几百万还没打通数据## 引言很多制造企业的信息中心负责人都听过一个数字&#xff1a;建一套数据中台&#xff0c;动辄投入三五百万元起步&#xff0c;周期一年到两年&#xff0c;最后能真正用起来的不超过一半。这不是某一家企业的问题&a…

作者头像 李华