尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

SpringMVC拦截器实战:从登录校验到接口限流

SpringMVC拦截器实战:从登录校验到接口限流 1. 项目概述SpringMVC拦截器的核心价值在构建一个Web应用时我们常常会遇到一些横切关注点比如权限校验、日志记录、性能监控、事务管理。这些功能逻辑往往需要分散在多个控制器方法中如果每个方法都写一遍不仅代码冗余维护起来也是个噩梦。SpringMVC拦截器Interceptor就是为了解决这类问题而生的一个核心组件。它允许你在请求到达控制器Controller之前、控制器处理之后、以及视图渲染完成之后这三个关键节点插入自定义的处理逻辑。简单来说拦截器就像是你家小区门口的保安。每一个访客HTTP请求要进入小区你的Web应用都必须经过保安亭。保安可以检查访客的证件权限校验、登记来访信息日志记录甚至决定是否放行拦截请求。这个“保安”的职责就是由拦截器来定义的。与Servlet规范中的过滤器Filter不同拦截器是Spring框架的一部分它能直接访问Spring的上下文比如获取Spring容器中的Bean因此与Spring MVC的集成度更高功能也更强大。对于正在使用或学习SpringMVC的开发者无论是处理用户登录状态、记录API访问日志、防止重复提交还是统一处理异常和响应格式拦截器都是一个必须掌握的工具。它能让你的代码结构更清晰业务逻辑更纯粹是构建健壮、可维护Web应用的基石。2. 拦截器整体设计与核心思路拆解2.1 拦截器与过滤器的本质区别很多刚接触的朋友容易把拦截器和Servlet过滤器搞混虽然它们都用于预处理和后处理请求但设计理念和应用场景有显著不同。理解这个区别是正确选用它们的关键。过滤器Filter是Java EEServlet规范的一部分它的工作层面更底层。过滤器作用于Servlet容器几乎可以拦截所有进入容器的请求和响应包括静态资源如.js,.css, 图片。它的能力强大但“粗犷”主要基于javax.servlet包下的API对Spring的上下文一无所知。拦截器Interceptor是Spring MVC框架的一部分它的工作层面在DispatcherServlet之后。这意味着一个请求会先经过过滤器链然后被DispatcherServlet接收进行HandlerMapping查找找到对应的处理器Handler后在执行处理器之前、之后以及完成之后才会轮到拦截器上场。因此拦截器只能拦截到那些被Spring MVC管理的请求即映射到了某个Controller的请求。它的优势在于深度集成Spring可以方便地使用Spring的依赖注入、AOP等特性。用一个生活化的类比你的Web应用是一栋大楼。过滤器是大楼外围的安检门所有想进大楼的人所有HTTP请求都必须过安检。拦截器则是各个部门Controller门口的接待员他只处理来找本部门办事的人映射到本Controller的请求并且能调用部门内部的资源Spring Bean来提供服务。2.2 SpringMVC拦截器的执行时机与工作流要玩转拦截器必须把它在SpringMVC整个请求处理流程中的位置刻在脑子里。一个典型的请求生命周期如下用户发送HTTP请求到服务器。Servlet容器如Tomcat接收到请求应用配置的过滤器链Filter Chain。请求到达DispatcherServletSpring MVC的前端控制器。DispatcherServlet调用HandlerMapping根据请求URL找到对应的处理器Handler和拦截器链Interceptor Chain。执行拦截器的preHandle方法。这是拦截器的第一个切入点。如果某个拦截器的preHandle返回false则请求被拦截后续的拦截器和控制器都不会执行流程直接跳到当前拦截器的afterCompletion如果有的话。如果所有拦截器的preHandle都返回trueDispatcherServlet调用处理器适配器HandlerAdapter来执行找到的控制器方法Controller Method。控制器方法执行完毕返回ModelAndView或ResponseBody直接写回数据。执行拦截器的postHandle方法。此时控制器方法已执行但视图还未渲染。你可以在这里对ModelAndView进行修改。DispatcherServlet进行视图解析和渲染如果是返回视图的话。请求完成后执行拦截器的afterCompletion方法。无论控制器执行成功还是抛出异常该方法都会被调用前提是preHandle返回了true。这里是进行资源清理、记录最终日志的理想场所。这个流程清晰地展示了拦截器三个核心方法的执行时机preHandle处理前、postHandle处理后视图渲染前、afterCompletion请求完成后。掌握这个流程你就能精准地把逻辑放在正确的位置。2.3 定义拦截器的两种方式HandlerInterceptor vs WebRequestInterceptorSpring主要提供了两种接口来定义拦截器它们各有侧重。HandlerInterceptor接口这是最常用、最经典的方式。它定义了三个方法boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)在控制器方法执行前调用。返回值决定是否继续执行后续拦截器和控制器。void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)在控制器方法执行后视图渲染前调用。可以对ModelAndView进行操作。void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)在整个请求结束后调用用于资源清理。ex参数包含了控制器执行过程中抛出的异常如果有。WebRequestInterceptor接口这个接口也定义了三个类似的方法但它的方法参数是WebRequest而不是原生的HttpServletRequest/Response。WebRequest是Spring对原生请求的一个抽象提供了一些便利方法。它的preHandle方法没有返回值意味着它无法直接拦截请求。通常需要与HandlerInterceptor配合使用或者用在特定的、不需要拦截功能的场景。实操心得99%的情况下直接使用HandlerInterceptor接口就足够了。它的preHandle方法能直接控制请求流程更为强大和直观。WebRequestInterceptor更像是一个适配器模式的应用当你需要一套不依赖于Servlet API的拦截逻辑时例如在非Web环境测试它才有用武之地。3. 核心细节解析与实操要点3.1 实现一个基础的登录校验拦截器理论说再多不如一行代码。我们来实现一个最经典的场景登录校验。假设我们的应用有些页面需要登录才能访问。首先创建一个类实现HandlerInterceptor接口package com.example.demo.interceptor; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; /** * 登录验证拦截器 */ public class LoginInterceptor implements HandlerInterceptor { /** * 在控制器方法执行前调用 * return true: 放行执行下一个拦截器或控制器 * false: 拦截后续流程不再执行 */ Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 1. 获取Session或从Token中获取用户信息 HttpSession session request.getSession(false); // false表示如果不存在则不创建新session // 2. 判断用户是否登录。这里假设登录后会在session中存入“user”属性 if (session ! null session.getAttribute(user) ! null) { // 用户已登录放行 return true; } // 3. 用户未登录根据请求类型返回不同响应 // 判断是否为Ajax请求通常前端框架会设置这个Header String xRequestedWith request.getHeader(X-Requested-With); if (XMLHttpRequest.equalsIgnoreCase(xRequestedWith)) { // Ajax请求返回JSON格式的未登录提示 response.setContentType(application/json;charsetutf-8); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // 401 未授权 response.getWriter().write({\code\: 401, \msg\: \用户未登录请先登录\}); } else { // 普通页面请求重定向到登录页 response.sendRedirect(request.getContextPath() /login); } // 4. 拦截请求 return false; } Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // 本例中不需要postHandle操作可以留空或写日志 // System.out.println(LoginInterceptor postHandle: 控制器执行完毕); } Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // 请求完成后的清理工作例如记录请求总耗时 // long startTime (Long) request.getAttribute(startTime); // long endTime System.currentTimeMillis(); // System.out.println(请求耗时: (endTime - startTime) ms); } }代码要点解析preHandle是关键我们主要的逻辑在这里。通过检查Session中是否存在用户信息来判断登录状态。区分请求类型这是一个非常重要的技巧。对于前后端分离的项目前端通过Ajax调用API如果返回一个重定向的HTML页面前端无法处理。因此我们通过检查请求头X-Requested-With来判断如果是Ajax请求则返回一个结构化的JSON错误信息如果是普通的页面跳转则重定向到登录页。这提升了用户体验和系统健壮性。postHandle和afterCompletion在这个简单的登录拦截器中我们可能用不到它们所以保持为空。但它们是扩展功能的入口。3.2 配置拦截器XML vs Java Config定义了拦截器还需要告诉Spring MVC在哪些路径下使用它。配置方式主要有两种。方式一传统的XML配置适用于老项目或偏好XML的项目在springmvc-servlet.xml或applicationContext.xml中添加mvc:interceptors !-- 配置一个全局拦截器拦截所有请求 -- !-- bean classcom.example.demo.interceptor.LoginInterceptor/ -- !-- 更常见的配置一个拦截器并指定拦截路径 -- mvc:interceptor !-- 匹配的路径模式支持Ant风格通配符 -- mvc:mapping path/user/**/ !-- 拦截/user下的所有请求 -- mvc:mapping path/order/**/ !-- 拦截/order下的所有请求 -- !-- 排除的路径 -- mvc:exclude-mapping path/user/login/ !-- 不拦截登录接口本身 -- mvc:exclude-mapping path/user/register/ mvc:exclude-mapping path/static/**/ !-- 不拦截静态资源 -- !-- 引用定义的拦截器Bean -- bean classcom.example.demo.interceptor.LoginInterceptor/ /mvc:interceptor !-- 可以配置多个拦截器它们将按配置顺序执行 -- mvc:interceptor mvc:mapping path/**/ bean classcom.example.demo.interceptor.LoggingInterceptor/ /mvc:interceptor /mvc:interceptors方式二基于Java的配置现代Spring Boot项目首选创建一个配置类实现WebMvcConfigurer接口并重写addInterceptors方法package com.example.demo.config; import com.example.demo.interceptor.LoginInterceptor; import com.example.demo.interceptor.LoggingInterceptor; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; Configuration public class WebMvcConfig implements WebMvcConfigurer { Override public void addInterceptors(InterceptorRegistry registry) { // 注册登录拦截器并配置拦截路径和排除路径 registry.addInterceptor(new LoginInterceptor()) .addPathPatterns(/user/**, /order/**) // 添加拦截路径 .excludePathPatterns( // 添加排除路径 /user/login, /user/register, /static/**, /error // 通常也需要排除错误页面 ) .order(1); // 设置拦截器执行顺序数字越小优先级越高 // 注册日志拦截器拦截所有请求 registry.addInterceptor(new LoggingInterceptor()) .addPathPatterns(/**) .order(2); // 顺序在登录拦截器之后 // 可以继续添加更多拦截器... } }注意事项路径匹配规则Spring MVC使用Ant风格路径模式。*匹配任意数量的字符但不包括目录分隔符/**匹配任意数量的字符包括目录分隔符。/user/*匹配/user/123但不匹配/user/123/profile/user/**则两者都匹配。排除静态资源务必记得排除对静态资源如图片、CSS、JS的拦截否则会影响页面加载。在Spring Boot中静态资源默认位于/static,/public,/resources,/META-INF/resources目录下对应的访问路径也需要排除。拦截器Bean的生命周期在Java Config中直接new出来的拦截器默认是单例的且不会被Spring容器管理即无法直接使用Autowired注入其他Bean。如果需要让拦截器成为Spring Bean以使用依赖注入可以将其声明为Component然后在配置类中通过Autowired注入。执行顺序order()方法的值越小拦截器的preHandle方法执行越早但它的postHandle和afterCompletion方法执行越晚。这符合“先进后出”的栈结构。3.3 拦截器链的执行顺序与中断当配置了多个拦截器时它们会形成一个拦截器链Interceptor Chain。理解链式执行顺序对于编写正确的拦截逻辑至关重要。假设我们配置了拦截器Aorder1和拦截器Border2对于一个匹配的请求执行顺序如下A.preHandle()- 如果返回true继续。B.preHandle()- 如果返回true继续。执行控制器方法。B.postHandle()- 执行。A.postHandle()- 执行。注意postHandle是逆序执行的视图渲染。B.afterCompletion()- 执行。A.afterCompletion()- 执行。afterCompletion也是逆序执行关键点在于中断如果A.preHandle()返回false那么B.preHandle()和控制器方法都不会执行。但是已经执行了preHandle且返回true的拦截器它的afterCompletion方法仍然会被调用。此时因为A.preHandle返回了false所以只有A.afterCompletion()会被调用如果A.preHandle执行了的话。B的任何一个方法都不会执行。这个机制要求我们在preHandle中执行资源初始化如开始计时并在afterCompletion中确保资源被清理如结束计时并记录即使请求被后续的拦截器拦截了。4. 高级应用与实战技巧4.1 在拦截器中注入Spring Bean与服务拦截器本身不是Controller或Service但很多时候我们需要在拦截器里调用业务服务比如根据Token查询用户详情。由于拦截器可能通过new创建默认不在Spring容器管理下无法直接使用Autowired。有两种主流解决方案方案一让拦截器本身成为Spring Bean这是最推荐的方式。使用Component注解标记拦截器然后在配置类中注入它。// 1. 拦截器类添加Component Component public class AuthInterceptor implements HandlerInterceptor { Autowired private UserService userService; // 可以正常注入 Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token request.getHeader(Authorization); if (token ! null) { User user userService.findUserByToken(token); // 使用注入的服务 if (user ! null) { request.setAttribute(currentUser, user); return true; } } response.sendError(HttpServletResponse.SC_UNAUTHORIZED, Invalid token); return false; } } // 2. 配置类中注入拦截器Bean Configuration public class WebMvcConfig implements WebMvcConfigurer { Autowired // 注入Spring容器中的拦截器Bean private AuthInterceptor authInterceptor; Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(authInterceptor) // 使用注入的Bean .addPathPatterns(/api/**); } }方案二通过WebMvcConfigurer的上下文获取Bean如果不想让拦截器成为Bean可以在配置方法中通过ApplicationContext获取。Configuration public class WebMvcConfig implements WebMvcConfigurer, ApplicationContextAware { private ApplicationContext applicationContext; Override public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext applicationContext; } Override public void addInterceptors(InterceptorRegistry registry) { // 手动从容器中获取Bean并设置到拦截器中 UserService userService applicationContext.getBean(UserService.class); AuthInterceptor interceptor new AuthInterceptor(); interceptor.setUserService(userService); // 需要拦截器有setter方法 registry.addInterceptor(interceptor) .addPathPatterns(/api/**); } }实操心得优先采用方案一。让拦截器成为Spring Bean是最符合Spring设计哲学的方式能享受到完整的依赖注入和生命周期管理。方案二显得有点“绕”仅在非常特殊的场景下比如拦截器需要动态创建或存在循环依赖时才考虑。4.2 使用拦截器实现接口访问频率限制防止恶意刷接口是Web安全的重要一环。我们可以利用拦截器配合缓存如Redis轻松实现一个简单的限流器。Component public class RateLimitInterceptor implements HandlerInterceptor { Autowired private RedisTemplateString, String redisTemplate; // 假设使用Redis private static final int LIMIT_COUNT 10; // 限制次数 private static final int LIMIT_SECONDS 60; // 时间窗口秒 Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String ip getClientIp(request); String key rate_limit: ip : request.getRequestURI(); // 使用Redis的INCR和EXPIRE命令实现滑动窗口计数 Long count redisTemplate.opsForValue().increment(key, 1); if (count ! null count 1) { // 第一次访问设置过期时间 redisTemplate.expire(key, LIMIT_SECONDS, TimeUnit.SECONDS); } if (count ! null count LIMIT_COUNT) { // 超过限制 response.setContentType(application/json;charsetutf-8); response.setStatus(HttpServletResponse.SC_TOO_MANY_REQUESTS); // 429 response.getWriter().write({\code\: 429, \msg\: \请求过于频繁请稍后再试\}); return false; } return true; } private String getClientIp(HttpServletRequest request) { // 一个简单的获取客户端IP的方法注意代理情况 String ip request.getHeader(X-Forwarded-For); if (ip null || ip.length() 0 || unknown.equalsIgnoreCase(ip)) { ip request.getHeader(Proxy-Client-IP); } if (ip null || ip.length() 0 || unknown.equalsIgnoreCase(ip)) { ip request.getHeader(WL-Proxy-Client-IP); } if (ip null || ip.length() 0 || unknown.equalsIgnoreCase(ip)) { ip request.getRemoteAddr(); } return ip; } }这个拦截器为每个IP和接口路径组合创建一个Redis键在1分钟的时间窗口内访问次数超过10次就会被拦截。这是一个非常基础的限流方案实际生产中可能需要更复杂的算法如令牌桶或漏桶算法但拦截器作为执行入口的思路是一致的。4.3 结合Annotation实现更细粒度的控制有时我们不想拦截整个Controller的所有方法或者不同的方法需要不同的拦截逻辑。这时可以结合自定义注解来实现。第一步定义一个注解Target(ElementType.METHOD) // 注解可以用在方法上 Retention(RetentionPolicy.RUNTIME) // 运行时保留 public interface NeedAuth { // 可以定义一些属性比如需要的权限角色 String[] roles() default {}; }第二步在Controller方法上使用注解RestController RequestMapping(/api/user) public class UserController { GetMapping(/profile) NeedAuth // 标记此方法需要认证 public ResponseEntityUserProfile getProfile() { // ... 业务逻辑 } GetMapping(/publicInfo) // 没有注解表示公开接口 public ResponseEntityPublicInfo getPublicInfo() { // ... 业务逻辑 } }第三步在拦截器中解析注解Component public class AnnotationAuthInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 判断handler是否是HandlerMethod映射到方法 if (handler instanceof HandlerMethod) { HandlerMethod handlerMethod (HandlerMethod) handler; // 获取方法上的NeedAuth注解 NeedAuth needAuth handlerMethod.getMethodAnnotation(NeedAuth.class); if (needAuth ! null) { // 该方法需要认证执行你的认证逻辑 if (!checkAuth(request)) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED); return false; } // 如果需要还可以进一步检查roles // String[] requiredRoles needAuth.roles(); // checkRoles(...) } // 如果没有注解直接放行 } return true; // 对于非方法处理器如资源处理器也直接放行 } private boolean checkAuth(HttpServletRequest request) { // 你的认证逻辑检查Session或Token // ... return true; // 或 false } }第四步配置拦截器拦截所有路径由于拦截逻辑由注解驱动这个拦截器可以配置为拦截所有请求/**然后在内部根据注解的有无来决定是否执行认证。这样控制权就从配置文件转移到了代码的注解上更加灵活和清晰。5. 常见问题与排查技巧实录在实际开发中使用拦截器可能会遇到一些“坑”。下面我整理了几个最常见的问题和解决方法。5.1 拦截器不生效的排查步骤这是新手最常遇到的问题。如果你的拦截器配置好了但没起作用请按以下顺序检查检查拦截器类是否被Spring管理如果你在Java Config中用new创建拦截器并且拦截器内部使用了Autowired那么注入肯定会失败拦截器逻辑可能因此抛出异常导致不生效。确保拦截器类被Component标注或在配置中正确初始化了其依赖。检查路径匹配是否正确这是最高频的原因。仔细核对addPathPatterns和excludePathPatterns。你的请求路径是否真的匹配了拦截模式注意路径的上下文Context Path。如果你的应用部署在/myapp下那么请求的完整路径是/myapp/api/xxx但Spring MVC匹配的是/api/xxx。是否不小心用excludePathPatterns排除了你的请求检查配置类是否被加载在Spring Boot中确保你的配置类带有Configuration和实现了WebMvcConfigurer位于主应用类SpringBootApplication的同包或子包下或者被显式地Import或ComponentScan扫描到。在传统XML项目中检查mvc:interceptors是否配置在了被加载的Spring配置文件中。检查是否有其他配置覆盖如果你同时存在多个WebMvcConfigurer的实现或者同时有XML和Java Config配置可能会发生覆盖。确保你的拦截器配置被最终应用。查看日志开启Spring的Debug日志logging.level.org.springframework.webDEBUG可以看到DispatcherServlet处理请求的详细过程包括匹配到的拦截器链。5.2 静态资源被拦截导致404这个问题太经典了。你配置了/**的拦截器然后发现页面上的图片、CSS、JS全都加载不了。原因Spring MVC中对静态资源的请求如图片/static/logo.png也会走DispatcherServlet如果被拦截器拦截而拦截器逻辑如登录检查不通过请求就被挡在外面了。解决方案在配置拦截器时务必排除静态资源的路径。Spring Boot默认静态资源路径/static/**,/public/**,/resources/**,/META-INF/resources/**。自定义静态资源路径如果你通过spring.mvc.static-path-pattern或WebMvcConfigurer.addResourceHandlers自定义了路径也需要一并排除。Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()) .addPathPatterns(/**) .excludePathPatterns( /login, /register, /static/**, /public/**, /css/**, /js/**, /img/**, /error // 排除错误页面避免错误页面也要求登录 ); }5.3 拦截器中抛出异常如何处理在preHandle、postHandle、afterCompletion中都有可能抛出运行时异常。preHandle中抛出异常该拦截器及后续拦截器的preHandle都不会执行控制器方法也不会执行。但之前已经成功执行了preHandle返回true的拦截器其afterCompletion方法仍然会被调用并且ex参数会携带这个异常。postHandle中抛出异常后续拦截器的postHandle不会执行视图渲染也不会进行。但所有拦截器的afterCompletion都会被调用。afterCompletion中抛出异常异常会被捕获并记录到日志但不会影响其他拦截器的afterCompletion执行。这是资源清理的最后机会应尽量避免在此处抛出异常。最佳实践在拦截器内部做好异常处理尤其是preHandle和postHandle中。如果发生非关键性异常如日志记录失败可以捕获并记录然后让流程继续如果是关键性错误如身份令牌无效则应设置好响应状态和内容后返回false对于preHandle而不是抛出异常让Spring的默认异常处理器处理因为那样你可能无法控制返回给客户端的响应格式。5.4 拦截器执行顺序与依赖问题当多个拦截器存在依赖关系时顺序至关重要。例如一个日志拦截器需要记录用户信息而用户信息需要在认证拦截器之后才能设置到请求中。控制顺序使用order()方法。值越小优先级越高preHandle越早执行。registry.addInterceptor(authInterceptor).order(1); // 先执行认证 registry.addInterceptor(loggingInterceptor).order(2); // 后执行日志此时request中已有用户信息依赖传递如果拦截器A依赖拦截器B设置到请求HttpServletRequest或会话HttpSession中的属性那么A的order必须大于B。因为B的preHandle先执行才能为A准备好数据。5.5 在拦截器中修改请求或响应有时我们可能想在拦截器中包装请求如读取请求体后多次使用或修改响应头。修改请求可以实现一个HttpServletRequestWrapper在preHandle中替换掉原来的request对象。但要注意这个包装后的request需要在过滤器链中更早的位置被设置通常这更适合用过滤器Filter来做。拦截器更侧重于业务逻辑的切入。修改响应可以直接操作HttpServletResponse对象如设置Header、Status、写入响应体等。这在preHandle拦截时返回错误信息和postHandle中很常见。一个典型的例子是统一设置响应头Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { // 统一设置API响应的Content-Type和缓存控制 response.setContentType(application/json;charsetUTF-8); response.setHeader(Cache-Control, no-cache, no-store, must-revalidate); response.setHeader(Pragma, no-cache); response.setDateHeader(Expires, 0); }拦截器是Spring MVC框架中一个强大而灵活的组件它将那些分散的、与核心业务无关的横切关注点集中管理极大地提升了代码的可维护性和可读性。从我多年的经验来看花时间设计好拦截器是构建一个整洁、健壮后端服务的必要投资。无论是权限、日志、限流还是数据预处理当你发现某个逻辑在多个Controller中重复出现时就应该考虑“是不是该把它放到拦截器里了”
返回列表