当前位置: 首页 > news >正文

第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 开发问题。


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

http://www.jsqmd.com/news/1345863/

相关文章:

  • PKC 第 031 个开关:关联上下文的位置、验证方法与风险边界
  • PDF 色彩保真工程实践【2】:用底层 PDF Object API 精准控制 PDF 色彩空间
  • 2026清河县服务好的全屋整装拎包入住优选推荐 - 谁都没有我好看
  • 手把手推导CNN与ViT的FLOPs与参数量:从原理到代码实战
  • 基于ReWorks与openEuler的全栈国产化无人智能控制系统架构实践
  • RT-Thread Env工具全解析:从环境配置到项目构建的嵌入式开发实践
  • LLM-based Zero-shot Triple Extraction for Automated Ontology Generation from Software Engineering St
  • 隔离性(Isolation)是数据库事务的ACID四大特性之一,指多个并发事务在执行过程中应相互隔离
  • ADC设备全解析:从核心原理到选型设计实战指南
  • SD卡电压切换:从协议到实战,解决嵌入式存储兼容性问题
  • Mac平台OpenCode开发环境部署与AI编程集成指南
  • MySQL数据库安全加固10大核心措施详解
  • 江苏海安实木家具定制工厂 整屋家具配套一站式服务 源头工厂直供 - 中国品牌企业观察网
  • 快速入门指南:用AKShare免费获取金融数据,3分钟开启量化研究
  • 物联网协议实战:从UDP/CoAP到MQTT/LwM2M的演进与混合架构设计
  • Java CompletableFuture 异步编程实战:从原理到多任务编排与性能优化
  • Windows驱动安装核心:INF文件结构、匹配逻辑与实战解析
  • 果园回声悖论:声波在非均匀介质中的传播特性研究
  • 阿里:迈向真实世界的GUI智能体
  • MySQL表字段批量修改实战与优化指南
  • 多变量LSTM实战:海上风电功率预测的工业数据分析全流程
  • 从PGC、UGC到AIGC:内容生产的三次浪潮与未来人机协同
  • GPU设备指定方法与性能优化实践指南
  • D3KeyHelper终极指南:解放双手的暗黑3自动化战斗解决方案
  • ARM Cortex-M硬错误诊断:CmBacktrace原理、移植与实战优化
  • VDA5050协议:构建工业级移动机器人集群统一通信架构的实践指南
  • 网盘直链下载助手完整教程:告别限速,轻松获取真实下载链接
  • 用Markdown驱动AI系统提示词:Nanobot项目解析与工程实践
  • RT-Thread AT组件驱动ESP8266:从原理到实战的嵌入式Wi-Fi开发指南
  • Claude 4.8架构升级:Prompt/Tool/Memory统一规范与工程化实践