模板下载网站什么好,国外网站推广,小程序推广怎么赚钱,河北怎样做网站一、什么是 Spring MVC 拦截器#xff1f;1.1 概念Spring MVC 的拦截器#xff08;Interceptor#xff09; 类似于 Servlet 规范中的 Filter#xff08;过滤器#xff09;#xff0c;但它是基于 Java 反射机制#xff08;AOP 思想#xff09;实现的#xff0c;主要用于…一、什么是 Spring MVC 拦截器1.1 概念Spring MVC 的拦截器Interceptor类似于 Servlet 规范中的 Filter过滤器但它是基于 Java 反射机制AOP 思想实现的主要用于对Controller 层的请求进行拦截和处理。它允许我们在请求被处理之前、处理之后以及视图渲染之后或者请求结束后执行特定的逻辑。1.2 核心作用请求预处理比如校验用户权限、记录请求日志、设置通用属性。请求后处理比如对 Controller 返回的 ModelAndView 进行加工。请求完成后的清理比如释放资源、记录请求耗时。二、拦截器接口HandlerInterceptorSpring MVC 定义了org.springframework.web.servlet.HandlerInterceptor接口它包含三个核心方法javapublic interface HandlerInterceptor { // 在 Handler 执行之前调用 default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return true; } // 在 Handler 执行之后视图渲染之前调用 default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, Nullable ModelAndView modelAndView) throws Exception { } // 在整个请求完成之后视图渲染之后调用通常用于资源清理 default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Nullable Exception ex) throws Exception { } }2.1 方法详解preHandle调用时机在HandlerMapping确定了具体的 Controller 方法即 Handler之后但在HandlerAdapter执行 Handler 之前。返回值boolean。返回true继续执行后续的拦截器和目标 Handler。返回false中断请求不再执行后续拦截器和 Handler此时需要由当前拦截器负责生成响应例如直接返回错误页面。常见用途权限校验、日志记录、参数校验。postHandle调用时机在 Handler 执行成功之后但在DispatcherServlet渲染视图View之前。注意如果preHandle返回false或者 Handler 执行过程中抛出异常postHandle不会被执行。常见用途对返回的ModelAndView进行额外处理如添加通用数据、修改视图名称。afterCompletion调用时机在整个请求结束之后即视图渲染完成通常用于清理资源。特点无论请求是否成功只要preHandle返回true该方法都会被执行。如果 Handler 执行过程中抛出异常异常对象会作为参数传入。常见用途记录请求耗时、释放资源如清除 ThreadLocal。三、拦截器的配置方式3.1 Java 配置Spring Boot / Spring 5 推荐通过实现WebMvcConfigurer接口重写addInterceptors方法。javaConfiguration public class WebConfig implements WebMvcConfigurer { Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new MyInterceptor()) // 添加自定义拦截器 .addPathPatterns(/**) // 拦截所有请求 .excludePathPatterns(/login, /register); // 排除登录注册 } }3.2 XML 配置传统 Spring MVC在 Spring MVC 配置文件中配置mvc:interceptors。xmlmvc:interceptors mvc:interceptor mvc:mapping path/**/ mvc:exclude-mapping path/login/ bean classcom.example.MyInterceptor/ /mvc:interceptor /mvc:interceptors四、拦截器的执行流程与原理4.1 核心组件回顾DispatcherServlet前端控制器所有请求的入口。HandlerMapping根据请求找到对应的 HandlerController 方法以及拦截器链封装成HandlerExecutionChain。HandlerExecutionChain包含了 Handler 对象和一组拦截器ListHandlerInterceptor。HandlerAdapter真正执行 Handler 的适配器。4.2 执行顺序正常流程假设我们有两个拦截器Interceptor1和Interceptor2按配置顺序。请求到达 DispatcherServlet。DispatcherServlet 调用 HandlerMapping获取HandlerExecutionChain其中包含 Handler 和拦截器列表顺序[Interceptor1, Interceptor2]。DispatcherServlet 按顺序调用拦截器的preHandle先执行Interceptor1.preHandle()返回true。再执行Interceptor2.preHandle()返回true。如果所有preHandle都返回true则继续。DispatcherServlet 通过 HandlerAdapter 执行 Handler即 Controller 方法。Handler 执行成功返回 ModelAndView。DispatcherServlet 逆序调用拦截器的postHandle先执行Interceptor2.postHandle()。再执行Interceptor1.postHandle()。DispatcherServlet 渲染视图。DispatcherServlet 逆序调用拦截器的afterCompletion先执行Interceptor2.afterCompletion()。再执行Interceptor1.afterCompletion()。响应返回给客户端。流程图文字版text请求 → DispatcherServlet → HandlerMapping获取执行链 → Interceptor1.preHandle → Interceptor2.preHandle → HandlerAdapter执行Controller → Interceptor2.postHandle → Interceptor1.postHandle → 视图渲染 → Interceptor2.afterCompletion → Interceptor1.afterCompletion → 响应4.3 异常流程情况1某个拦截器的preHandle返回 false假设Interceptor2.preHandle()返回false则立即停止执行后续拦截器的preHandle也不再执行 Handler。仅逆序调用已经执行过preHandle且返回true的拦截器的afterCompletion此处只有Interceptor1的afterCompletion。然后直接返回响应由Interceptor2的preHandle中自行处理响应或 DispatcherServlet 默认返回空。情况2Handler 执行抛出异常如果preHandle全部通过但 Handler 执行时抛出异常则不会执行任何postHandle因为请求处理失败视图未生成。DispatcherServlet 会触发逆序调用那些preHandle返回true的拦截器的afterCompletion并将异常对象传入。然后 DispatcherServlet 会处理异常例如通过ExceptionHandler或默认错误页面。4.4 源码视角DispatcherServlet 中的拦截器调用我们来看DispatcherServlet的核心方法doDispatch简化版javaprotected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest request; HandlerExecutionChain mappedHandler null; ModelAndView mv null; Exception dispatchException null; try { // 1. 通过 HandlerMapping 获取执行链包含拦截器 mappedHandler getHandler(processedRequest); if (mappedHandler null) { noHandlerFound(processedRequest, response); return; } // 2. 获取 HandlerAdapter HandlerAdapter ha getHandlerAdapter(mappedHandler.getHandler()); // 3. 执行拦截器的 preHandle 方法顺序 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; // 如果有拦截器返回 false直接返回 } // 4. 真正执行 HandlerController 方法 mv ha.handle(processedRequest, response, mappedHandler.getHandler()); // 5. 执行拦截器的 postHandle 方法逆序 mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException ex; } finally { // 6. 处理异常并执行 afterCompletion processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } }在HandlerExecutionChain中applyPreHandle、applyPostHandle和triggerAfterCompletion的实现如下java// 顺序执行 preHandle boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { for (int i 0; i this.interceptorList.size(); i) { HandlerInterceptor interceptor this.interceptorList.get(i); if (!interceptor.preHandle(request, response, this.handler)) { // 如果某个返回 false触发 afterCompletion仅对已经执行过的拦截器 triggerAfterCompletion(request, response, null); return false; } this.interceptorIndex i; // 记录最后一个执行成功的索引 } return true; } // 逆序执行 postHandle void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception { for (int i this.interceptorList.size() - 1; i 0; i--) { HandlerInterceptor interceptor this.interceptorList.get(i); interceptor.postHandle(request, response, this.handler, mv); } } // 逆序执行 afterCompletion void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex) { for (int i this.interceptorIndex; i 0; i--) { HandlerInterceptor interceptor this.interceptorList.get(i); try { interceptor.afterCompletion(request, response, this.handler, ex); } catch (Throwable ex2) { logger.error(HandlerInterceptor.afterCompletion threw exception, ex2); } } }注意interceptorIndex的作用它记录了成功执行preHandle的最后一个拦截器的索引这样在异常或提前返回时可以确保只对已执行的部分进行afterCompletion。五、拦截器的应用场景5.1 日志记录记录每个请求的 URL、IP、参数、处理时间等。javapublic class LogInterceptor implements HandlerInterceptor { private static final Logger log LoggerFactory.getLogger(LogInterceptor.class); private static final ThreadLocalLong startTime new ThreadLocal(); Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { startTime.set(System.currentTimeMillis()); log.info(请求URL: {}, 方法: {}, IP: {}, request.getRequestURL(), request.getMethod(), request.getRemoteAddr()); return true; } Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { long duration System.currentTimeMillis() - startTime.get(); log.info(请求完成耗时: {} ms, duration); startTime.remove(); // 防止内存泄漏 } }5.2 权限验证检查用户是否已登录或者是否具有访问某个资源的权限。javapublic class AuthInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session request.getSession(false); if (session ! null session.getAttribute(user) ! null) { return true; // 已登录放行 } // 未登录重定向到登录页 response.sendRedirect(/login); return false; } }5.3 修改或增强 ModelAndView在所有响应中添加公共数据比如网站名称、版本号等。javapublic class CommonDataInterceptor implements HandlerInterceptor { Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { if (modelAndView ! null) { modelAndView.addObject(siteName, MyApp); modelAndView.addObject(version, 1.0); } } }5.4 API 限流可以在preHandle中基于令牌桶或计数器实现简单的限流逻辑如果超过阈值则返回错误响应。5.5 跨域处理CORS虽然 Spring 有专门的 CORS 配置但也可以利用拦截器设置响应头。javaOverride public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { response.setHeader(Access-Control-Allow-Origin, *); return true; }六、拦截器与 Filter 的区别面试中经常会被问到两者的区别以下是详细对比维度拦截器 (Interceptor)过滤器 (Filter)规范Spring MVC 框架内部实现基于 Spring 容器Servlet 规范的一部分与 Spring 无关适用范围只能拦截 Spring MVC 的请求即经过 DispatcherServlet 的请求能过滤几乎所有请求包括静态资源、Servlet、JSP 等范围更广依赖容器依赖 Spring IoC 容器可以方便地注入 Bean依赖 Servlet 容器但也可以配置为 Spring Bean通过 DelegatingFilterProxy执行时机在 HandlerMapping 确定 Handler 之后HandlerAdapter 执行前后在请求进入 Servlet 容器后到达 Servlet 之前以及在 Servlet 处理完之后返回客户端之前粒度可以访问 Handler 对象、ModelAndView 等 Spring MVC 组件只能访问 HttpServletRequest 和 HttpServletResponse异常处理可以通过afterCompletion捕获 Handler 执行时的异常无法直接捕获 Spring MVC 处理器中的异常但可以捕获整个 Servlet 请求过程中的异常配置方式通过 Spring MVC 配置实现 WebMvcConfigurer 或 XML通过 web.xml 或WebFilter注解执行顺序通过配置顺序决定且 pre 顺序执行post 逆序执行通过filter-mapping的dispatcher和顺序决定中断请求通过preHandle返回false中断并可以自行处理响应通过chain.doFilter不调用后续过滤器或目标资源来中断总结Filter 更适合处理与 Servlet 规范相关的通用任务如字符编码、跨域、安全认证而 Interceptor 更适合处理与 Spring MVC 控制器相关的任务如权限校验、日志、修改视图数据。七、高级话题7.1 异步请求中的拦截器Spring MVC 3.2 开始支持异步请求处理例如返回Callable或DeferredResult。对于异步请求拦截器的行为有所不同preHandle仍然在请求处理线程中执行。postHandle和afterCompletion在异步请求的主线程中执行实际上对于异步请求postHandle是在异步任务提交之后但在结果处理之前执行的而afterCompletion会在异步请求完全结束包括结果处理后执行。为了更细粒度地控制异步请求Spring 提供了AsyncHandlerInterceptor它继承了HandlerInterceptor并增加了afterConcurrentHandlingStarted方法该方法在异步请求开始时即线程切换时被调用可以用来清理线程局部变量等。javapublic interface AsyncHandlerInterceptor extends HandlerInterceptor { default void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { } }7.2 多个拦截器的执行顺序陷阱如果拦截器 A 依赖于拦截器 B 的执行结果那么必须注意配置顺序。因为preHandle是按配置顺序执行的而postHandle和afterCompletion是逆序的。例如配置顺序A 在前B 在后。执行preHandleA → BpostHandleB → AafterCompletionB → A。如果 B 的postHandle依赖于 A 的某些处理结果那么这种逆序可能造成问题。因此要确保设计合理。7.3 拦截器与 Spring SecuritySpring Security 的过滤器链如UsernamePasswordAuthenticationFilter本质上是 Filter在拦截器之前执行。但 Spring Security 也提供了类似拦截器的机制如PreAuthorize注解这些是通过 AOP 实现的与 HandlerInterceptor 不同。在架构上Spring Security 的过滤器负责认证而方法级别的安全注解负责授权。拦截器可以用于补充一些基于请求的权限校验。7.4 性能监控与链路追踪利用preHandle和afterCompletion可以很方便地实现请求耗时的监控并结合MDCMapped Diagnostic Context将 Trace ID 放入日志实现分布式链路追踪。八、自定义拦截器示例综合案例假设我们需要一个拦截器完成以下功能打印请求日志包括请求参数。校验用户是否已登录Session 中存在 user 属性。记录请求耗时。在视图渲染前添加公共数据。javaComponent public class AppInterceptor implements HandlerInterceptor { private static final Logger log LoggerFactory.getLogger(AppInterceptor.class); private static final ThreadLocalLong startTime new ThreadLocal(); Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 1. 记录开始时间 startTime.set(System.currentTimeMillis()); // 2. 打印请求日志 String uri request.getRequestURI(); String method request.getMethod(); String params request.getQueryString(); log.info(请求开始: [{}] {}, 参数: {}, method, uri, params); // 3. 权限校验假设登录用户存储在 session 的 user 属性中 HttpSession session request.getSession(false); if (session null || session.getAttribute(user) null) { // 未登录返回 401 或重定向 response.sendError(HttpServletResponse.SC_UNAUTHORIZED, 请先登录); return false; // 中断 } return true; } Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // 添加公共数据到 ModelAndView if (modelAndView ! null) { modelAndView.addObject(appName, MySpringApp); modelAndView.addObject(year, Calendar.getInstance().get(Calendar.YEAR)); } } Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { long duration System.currentTimeMillis() - startTime.get(); log.info(请求结束: {} {} , 耗时: {} ms, 状态码: {}, request.getMethod(), request.getRequestURI(), duration, response.getStatus()); startTime.remove(); // 防止内存泄漏 } }在配置类中注册javaConfiguration public class WebConfig implements WebMvcConfigurer { Autowired private AppInterceptor appInterceptor; Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(appInterceptor) .addPathPatterns(/**) .excludePathPatterns(/login, /error, /static/**); } }九、常见问题与注意事项9.1 拦截器不生效的可能原因没有正确配置拦截器未添加到InterceptorRegistry或配置路径错误。请求被其他 Filter 提前拦截或处理。拦截器的preHandle返回了false但未设置响应导致前端无响应。使用了EnableWebMvc但未继承WebMvcConfigurer或配置类未被扫描。9.2 拦截器中获取 Handler 方法信息preHandle的参数handler通常是HandlerMethod类型可以获取 Controller 类名和方法名用于精细化的权限控制。javaif (handler instanceof HandlerMethod) { HandlerMethod method (HandlerMethod) handler; String controllerName method.getBeanType().getSimpleName(); String methodName method.getMethod().getName(); log.info(目标控制器: {}, 方法: {}, controllerName, methodName); }9.3 拦截器与全局异常处理如果 Handler 抛出异常afterCompletion会接收到异常对象但postHandle不会执行。异常最终会被DispatcherServlet的processDispatchResult处理可能进入ExceptionHandler或HandlerExceptionResolver。因此在afterCompletion中只能记录异常不能改变响应结果因为响应可能已经提交。9.4 响应流问题如果在preHandle中直接向 response 写入内容如 JSON 错误信息需要注意必须设置正确的 Content-Type。不要调用后续的chain.doFilter这里指拦截器链或返回false。但要注意如果 response 已经提交例如调用了getWriter()并写入数据后续的postHandle和afterCompletion可能无法再修改响应。同时视图渲染阶段可能抛出IllegalStateException。所以如果需要自定义错误响应建议在preHandle中返回false并直接写入响应后立即返回。十、总结Spring MVC 拦截器是基于 AOP 思想、通过 HandlerExecutionChain 实现的请求拦截机制。它提供了preHandle、postHandle、afterCompletion三个切入点允许开发者在 Controller 处理请求的前后插入自定义逻辑。拦截器的配置灵活可以精细控制拦截路径并且天然与 Spring IoC 集成方便注入各种 Bean。理解拦截器的原理关键是掌握其在 DispatcherServlet 流程中的位置以及多个拦截器的执行顺序。此外了解它与 Filter 的区别有助于在合适的场景选择合适的工具。