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

05.SpringBoot拦截器的使用详解

文章目录

    • 1. 拦截器基础概念
      • 1.1 什么是拦截器(Interceptor)
      • 1.2 拦截器的作用
      • 1.3 拦截器 vs 过滤器
    • 2. 拦截器的工作原理
      • 2.1 拦截器的执行流程
      • 2.2 拦截器的三个核心方法
    • 3. 创建自定义拦截器
      • 3.1 实现HandlerInterceptor接口
      • 3.2 继承HandlerInterceptorAdapter类(已过时)
    • 4. 注册拦截器
      • 4.1 通过配置类注册
      • 4.2 拦截器路径配置详解
    • 5. 实际应用示例
      • 5.1 登录验证拦截器
      • 5.2 权限验证拦截器
      • 5.3 接口限流拦截器
    • 6. 跨域处理拦截器
    • 7. 多拦截器配置和执行顺序
      • 7.1 拦截器执行顺序控制
      • 7.2 多拦截器执行流程示例
    • 8. 拦截器中的异常处理
      • 8.1 异常处理拦截器
      • 8.2 拦截器中的异常捕获
    • 9. 拦截器与注解结合使用
      • 9.1 创建自定义注解
      • 9.2 基于注解的拦截器
      • 9.3 使用示例
    • 10. 性能监控拦截器
    • 11. 测试拦截器
      • 11.1 创建测试控制器
      • 11.2 性能统计接口
      • 11.3 测试拦截器功能
    • 12. 拦截器与Spring Security集成
      • 12.1 与Spring Security协同工作
    • 13. 拦截器配置的最佳实践
      • 13.1 环境相关的拦截器配置
      • 13.2 拦截器配置外部化
    • 14. 常见问题和解决方案
      • 14.1 拦截器不生效的问题
      • 14.2 拦截器执行顺序问题
      • 14.3 静态资源被拦截的问题
      • 14.4 无法获取请求体的问题
      • 14.5 响应已提交的问题
    • 15. 性能优化建议
      • 15.1 拦截器性能优化
    • 16. 拦截器单元测试
      • 16.1 拦截器单元测试示例
      • 16.2 集成测试
    • 17. 最佳实践总结
      • 17.1 设计原则
      • 17.2 代码组织
      • 17.3 命名规范
      • 17.4 监控和调试
    • 18. 总结
      • 18.1 核心知识点
      • 18.2 实际应用场景
      • 18.3 高级特性
      • 18.4 学习建议

1. 拦截器基础概念

1.1 什么是拦截器(Interceptor)

拦截器(Interceptor)是Spring MVC框架提供的一种组件,它可以在请求处理的不同阶段进行拦截和处理。拦截器基于Java的反射机制和动态代理,是AOP(面向切面编程)思想的具体应用。

形象比喻:
想象拦截器就像是公司大楼的多道安检门:

  • 第一道门(preHandle):检查你的工作证,决定是否让你进入
  • 办事过程(Controller处理):你在各个部门办事
  • 第二道门(postHandle):办完事后,保安检查你的包裹,但还没离开大楼
  • 第三道门(afterCompletion):完全离开大楼前的最后检查

1.2 拦截器的作用

  1. 权限验证:检查用户是否有权限访问某个资源
  2. 登录验证:验证用户是否已登录
  3. 日志记录:记录请求的详细信息
  4. 性能监控:统计接口执行时间
  5. 参数预处理:对请求参数进行统一处理
  6. 响应处理:对响应结果进行统一格式化
  7. 异常处理:统一处理异常情况

1.3 拦截器 vs 过滤器

特性拦截器(Interceptor)过滤器(Filter)
基于框架Spring MVCServlet规范
拦截范围只拦截Controller请求拦截所有Web请求
执行时机DispatcherServlet内部Servlet容器级别
获取Spring Bean容易(天然支持)需要特殊处理
访问ModelAndView可以不可以
配置方式实现HandlerInterceptor实现Filter接口

2. 拦截器的工作原理

2.1 拦截器的执行流程

客户端请求 → DispatcherServlet → 拦截器1.preHandle → 拦截器2.preHandle → Controller → 
拦截器2.postHandle → 拦截器1.postHandle → 视图渲染 → 
拦截器2.afterCompletion → 拦截器1.afterCompletion → 响应返回

2.2 拦截器的三个核心方法

  1. preHandle:在Controller方法执行前调用

    • 返回true:继续执行
    • 返回false:中断执行
  2. postHandle:在Controller方法执行后、视图渲染前调用

    • 可以修改ModelAndView
  3. afterCompletion:在整个请求完成后调用

    • 无论是否出现异常都会执行
    • 通常用于资源清理

3. 创建自定义拦截器

3.1 实现HandlerInterceptor接口

这是最常用的方式:

package com.example.interceptor;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** 日志拦截器 - 记录请求的基本信息* 实现HandlerInterceptor接口*/
@Component
public class LogInterceptor implements HandlerInterceptor {private static final Logger logger = LoggerFactory.getLogger(LogInterceptor.class);/*** 在Controller方法执行前调用* @param request 请求对象* @param response 响应对象  * @param handler 处理器对象(通常是Controller中的方法)* @return true表示继续执行,false表示中断执行*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 记录请求开始时间long startTime = System.currentTimeMillis();request.setAttribute("startTime", startTime);// 获取请求信息String method = request.getMethod();String uri = request.getRequestURI();String clientIP = request.getRemoteAddr();String userAgent = request.getHeader("User-Agent");logger.info("请求开始 - 方法: {}, URI: {}, IP: {}, UserAgent: {}", method, uri, clientIP, userAgent);// 返回true表示继续执行后续的拦截器和Controllerreturn true;}/*** 在Controller方法执行后、视图渲染前调用* @param request 请求对象* @param response 响应对象* @param handler 处理器对象* @param modelAndView 模型和视图对象*/@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {String uri = request.getRequestURI();logger.info("Controller执行完成 - URI: {}", uri);// 可以在这里修改ModelAndViewif (modelAndView != null) {// 添加公共数据到模型中modelAndView.addObject("currentTime", System.currentTimeMillis());}}/*** 在整个请求完成后调用(视图渲染完成后)* 无论是否出现异常都会执行* @param request 请求对象* @param response 响应对象* @param handler 处理器对象* @param ex 异常对象(如果有异常的话)*/@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// 计算请求处理时间Long startTime = (Long) request.getAttribute("startTime");if (startTime != null) {long endTime = System.currentTimeMillis();long responseTime = endTime - startTime;String uri = request.getRequestURI();logger.info("请求完成 - URI: {}, 耗时: {} ms", uri, responseTime);}// 如果有异常,记录异常信息if (ex != null) {logger.error("请求处理过程中发生异常: {}", ex.getMessage(), ex);}// 清理资源(如果需要的话)request.removeAttribute("startTime");}
}

3.2 继承HandlerInterceptorAdapter类(已过时)

注意:在Spring 5.3以后,HandlerInterceptorAdapter已被标记为过时,推荐直接实现HandlerInterceptor接口。

4. 注册拦截器

4.1 通过配置类注册

创建一个配置类来注册拦截器:

package com.example.config;import com.example.interceptor.LogInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*** Web配置类 - 注册拦截器*/
@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate LogInterceptor logInterceptor;/*** 注册拦截器* @param registry 拦截器注册器*/@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(logInterceptor).addPathPatterns("/**")          // 拦截所有路径.excludePathPatterns(           // 排除特定路径"/static/**",               // 静态资源"/css/**","/js/**", "/images/**","/favicon.ico","/error"                    // 错误页面);}
}

4.2 拦截器路径配置详解

@Configuration
public class WebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 示例1:拦截所有路径registry.addInterceptor(new LogInterceptor()).addPathPatterns("/**");// 示例2:只拦截API路径registry.addInterceptor(new AuthInterceptor()).addPathPatterns("/api/**");// 示例3:拦截多个特定路径registry.addInterceptor(new SecurityInterceptor()).addPathPatterns("/admin/**", "/manage/**");// 示例4:复杂的包含和排除配置registry.addInterceptor(new ComplexInterceptor()).addPathPatterns("/**")                    // 包含所有.excludePathPatterns(                      // 排除以下路径"/public/**",                         // 公共资源"/login",                             // 登录页面"/register",                          // 注册页面"/static/**",                         // 静态资源"/webjars/**",                        // WebJars资源"/swagger-ui/**",                     // Swagger UI"/v3/api-docs/**"                     // API文档);}
}

5. 实际应用示例

5.1 登录验证拦截器

package com.example.interceptor;import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;/*** 登录验证拦截器* 检查用户是否已登录*/
@Component
public class LoginInterceptor implements HandlerInterceptor {@Autowiredprivate ObjectMapper objectMapper;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 获取sessionHttpSession session = request.getSession(false);// 检查session中是否有用户信息if (session == null || session.getAttribute("user") == null) {// 未登录,根据请求类型返回不同响应if (isAjaxRequest(request)) {// AJAX请求,返回JSON响应response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);response.setContentType("application/json;charset=UTF-8");Map<String, Object> result = new HashMap<>();result.put("code", 401);result.put("message", "请先登录");result.put("redirectUrl", "/login");response.getWriter().write(objectMapper.writeValueAsString(result));} else {// 普通请求,重定向到登录页面response.sendRedirect("/login?returnUrl=" + request.getRequestURI());}return false;  // 中断执行}// 已登录,继续执行return true;}/*** 判断是否是AJAX请求*/private boolean isAjaxRequest(HttpServletRequest request) {String contentType = request.getContentType();String accept = request.getHeader("Accept");String xRequestedWith = request.getHeader("X-Requested-With");return "XMLHttpRequest".equals(xRequestedWith) ||(contentType != null && contentType.contains("application/json")) ||(accept != null && accept.contains("application/json"));}
}

5.2 权限验证拦截器

package com.example.interceptor;import com.example.annotation.RequirePermission;
import com.example.entity.User;
import com.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;/*** 权限验证拦截器* 检查用户是否有执行某个操作的权限*/
@Component
public class PermissionInterceptor implements HandlerInterceptor {@Autowiredprivate UserService userService;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 只处理Controller方法if (!(handler instanceof HandlerMethod)) {return true;}HandlerMethod handlerMethod = (HandlerMethod) handler;// 检查方法是否有权限注解RequirePermission permissionAnnotation = handlerMethod.getMethodAnnotation(RequirePermission.class);if (permissionAnnotation == null) {// 没有权限注解,直接通过return true;}// 获取当前用户HttpSession session = request.getSession(false);if (session == null) {sendForbiddenResponse(response, "用户未登录");return false;}User currentUser = (User) session.getAttribute("user");if (currentUser == null) {sendForbiddenResponse(response, "用户未登录");return false;}// 检查用户权限String requiredPermission = permissionAnnotation.value();boolean hasPermission = userService.hasPermission(currentUser.getId(), requiredPermission);if (!hasPermission) {sendForbiddenResponse(response, "权限不足");return false;}return true;}/*** 发送403响应*/private void sendForbiddenResponse(HttpServletResponse response, String message) throws Exception {response.setStatus(HttpServletResponse.SC_FORBIDDEN);response.setContentType("application/json;charset=UTF-8");response.getWriter().write(String.format("{\"code\":403,\"message\":\"%s\"}", message));}
}

配套的权限注解:

package com.example.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 权限验证注解* 用于标记需要特定权限才能访问的方法*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequirePermission {/*** 需要的权限代码*/String value();/*** 权限描述*/String description() default "";
}

使用示例:

@RestController
@RequestMapping("/admin")
public class AdminController {@GetMapping("/users")@RequirePermission("USER_VIEW")public List<User> getUsers() {// 需要USER_VIEW权限才能访问return userService.getAllUsers();}@DeleteMapping("/users/{id}")@RequirePermission("USER_DELETE")public String deleteUser(@PathVariable Long id) {// 需要USER_DELETE权限才能访问userService.deleteUser(id);return "删除成功";}
}

5.3 接口限流拦截器

package com.example.interceptor;import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;/*** 接口限流拦截器* 基于IP和接口路径进行限流*/
@Component
public class RateLimitInterceptor implements HandlerInterceptor {// 存储每个IP+接口的访问次数private final ConcurrentHashMap<String, RateLimitInfo> rateLimitMap = new ConcurrentHashMap<>();// 时间窗口大小(毫秒)private static final long TIME_WINDOW = 60000; // 1分钟// 每个时间窗口内允许的最大请求数private static final int MAX_REQUESTS = 100;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String clientIP = getClientIP(request);String uri = request.getRequestURI();String key = clientIP + ":" + uri;// 检查是否触发限流if (isRateLimited(key)) {response.setStatus(429); // Too Many Requestsresponse.setContentType("application/json;charset=UTF-8");response.setHeader("Retry-After", "60");response.getWriter().write("{\"code\":429,\"message\":\"请求过于频繁,请稍后再试\"}");return false;}return true;}/*** 检查是否触发限流*/private boolean isRateLimited(String key) {long currentTime = System.currentTimeMillis();RateLimitInfo rateLimitInfo = rateLimitMap.computeIfAbsent(key, k -> new RateLimitInfo());synchronized (rateLimitInfo) {// 如果当前时间超过时间窗口,重置计数if (currentTime - rateLimitInfo.getWindowStart() > TIME_WINDOW) {rateLimitInfo.setWindowStart(currentTime);rateLimitInfo.getRequestCount().set(0);}// 增加请求计数int currentCount = rateLimitInfo.getRequestCount().incrementAndGet();// 返回是否超过限制return currentCount > MAX_REQUESTS;}}/*** 获取客户端真实IP*/private String getClientIP(HttpServletRequest request) {String ip = request.getHeader("X-Forwarded-For");if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("Proxy-Client-IP");}if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("WL-Proxy-Client-IP");}if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {ip = request.getRemoteAddr();}return ip;}/*** 限流信息内部类*/private static class RateLimitInfo {private volatile long windowStart = System.currentTimeMillis();private final AtomicInteger requestCount = new AtomicInteger(0);public long getWindowStart() {return windowStart;}public void setWindowStart(long windowStart) {this.windowStart = windowStart;}public AtomicInteger getRequestCount() {return requestCount;}}
}

6. 跨域处理拦截器

package com.example.interceptor;import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.List;/*** 跨域处理拦截器* 处理CORS跨域请求*/
@Component
public class CorsInterceptor implements HandlerInterceptor {// 允许跨域的域名列表private static final List<String> ALLOWED_ORIGINS = Arrays.asList("http://localhost:3000","http://localhost:8080", "https://yourdomain.com");@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String origin = request.getHeader("Origin");// 检查Origin是否在允许列表中if (origin != null && ALLOWED_ORIGINS.contains(origin)) {response.setHeader("Access-Control-Allow-Origin", origin);}// 设置其他CORS响应头response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With");response.setHeader("Access-Control-Allow-Credentials", "true");response.setHeader("Access-Control-Max-Age", "3600");// 处理预检请求(OPTIONS)if ("OPTIONS".equals(request.getMethod())) {response.setStatus(HttpServletResponse.SC_OK);return false; // 不继续执行,直接返回}return true;}
}

7. 多拦截器配置和执行顺序

7.1 拦截器执行顺序控制

@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate LogInterceptor logInterceptor;@Autowiredprivate LoginInterceptor loginInterceptor;@Autowiredprivate PermissionInterceptor permissionInterceptor;@Autowiredprivate RateLimitInterceptor rateLimitInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 1. 日志拦截器 - 最高优先级,记录所有请求registry.addInterceptor(logInterceptor).addPathPatterns("/**").order(1);// 2. 限流拦截器 - 第二优先级,防止恶意请求registry.addInterceptor(rateLimitInterceptor).addPathPatterns("/api/**").order(2);// 3. 登录验证拦截器 - 第三优先级registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns("/login", "/register", "/public/**", "/static/**", "/error").order(3);// 4. 权限验证拦截器 - 最后执行registry.addInterceptor(permissionInterceptor).addPathPatterns("/admin/**", "/api/**").order(4);}
}

7.2 多拦截器执行流程示例

假设有3个拦截器A、B、C,执行顺序如下:

请求到达 → A.preHandle → B.preHandle → C.preHandle → Controller执行 
→ C.postHandle → B.postHandle → A.postHandle → 视图渲染 
→ C.afterCompletion → B.afterCompletion → A.afterCompletion → 响应返回

注意事项:

  • 如果某个拦截器的preHandle返回false,后续的拦截器和Controller都不会执行
  • 但已经执行过preHandle的拦截器的afterCompletion方法仍会执行

8. 拦截器中的异常处理

8.1 异常处理拦截器

package com.example.interceptor;import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;/*** 异常处理拦截器* 统一处理请求过程中的异常*/
@Component
public class ExceptionInterceptor implements HandlerInterceptor {private static final Logger logger = LoggerFactory.getLogger(ExceptionInterceptor.class);@Autowiredprivate ObjectMapper objectMapper;@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {if (ex != null) {// 记录异常信息logger.error("请求处理过程中发生异常 - URI: {}, 异常: {}", request.getRequestURI(), ex.getMessage(), ex);// 如果响应还没有被提交,返回统一的错误响应if (!response.isCommitted()) {response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);response.setContentType("application/json;charset=UTF-8");Map<String, Object> errorResponse = new HashMap<>();errorResponse.put("code", 500);errorResponse.put("message", "服务器内部错误");errorResponse.put("timestamp", System.currentTimeMillis());// 在开发环境可以返回详细异常信息if (isDevelopmentMode()) {errorResponse.put("error", ex.getMessage());errorResponse.put("path", request.getRequestURI());}response.getWriter().write(objectMapper.writeValueAsString(errorResponse));}}}/*** 判断是否是开发模式*/private boolean isDevelopmentMode() {// 这里可以通过配置文件或环境变量来判断return "development".equals(System.getProperty("spring.profiles.active"));}
}

8.2 拦截器中的异常捕获

@Component
public class SafeInterceptor implements HandlerInterceptor {private static final Logger logger = LoggerFactory.getLogger(SafeInterceptor.class);@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {try {// 具体的拦截逻辑return doPreHandle(request, response, handler);} catch (Exception e) {logger.error("拦截器执行异常", e);// 返回错误响应response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);response.setContentType("application/json;charset=UTF-8");response.getWriter().write("{\"code\":500,\"message\":\"系统异常\"}");return false; // 中断执行}}private boolean doPreHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 实际的业务逻辑return true;}
}

9. 拦截器与注解结合使用

9.1 创建自定义注解

package com.example.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 跳过登录验证注解* 标记不需要登录验证的方法*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SkipLogin {/*** 跳过原因说明*/String reason() default "";
}
package com.example.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 操作日志注解* 记录用户的操作行为*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OperationLog {/*** 操作类型*/String operation();/*** 操作描述*/String description() default "";/*** 是否记录参数*/boolean logParams() default true;/*** 是否记录返回值*/boolean logResult() default false;
}

9.2 基于注解的拦截器

package com.example.interceptor;import com.example.annotation.OperationLog;
import com.example.annotation.SkipLogin;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;/*** 基于注解的智能拦截器*/
@Component
public class AnnotationBasedInterceptor implements HandlerInterceptor {private static final Logger logger = LoggerFactory.getLogger(AnnotationBasedInterceptor.class);@Autowiredprivate ObjectMapper objectMapper;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if (!(handler instanceof HandlerMethod)) {return true;}HandlerMethod handlerMethod = (HandlerMethod) handler;Method method = handlerMethod.getMethod();// 检查是否跳过登录验证if (!shouldSkipLogin(handlerMethod) && !isUserLoggedIn(request)) {response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);response.setContentType("application/json;charset=UTF-8");response.getWriter().write("{\"code\":401,\"message\":\"请先登录\"}");return false;}// 处理操作日志注解OperationLog operationLog = method.getAnnotation(OperationLog.class);if (operationLog != null) {logOperation(request, operationLog);}return true;}/*** 检查是否应该跳过登录验证*/private boolean shouldSkipLogin(HandlerMethod handlerMethod) {// 检查方法级别的注解if (handlerMethod.getMethodAnnotation(SkipLogin.class) != null) {return true;}// 检查类级别的注解if (handlerMethod.getBeanType().getAnnotation(SkipLogin.class) != null) {return true;}return false;}/*** 检查用户是否已登录*/private boolean isUserLoggedIn(HttpServletRequest request) {HttpSession session = request.getSession(false);return session != null && session.getAttribute("user") != null;}/*** 记录操作日志*/private void logOperation(HttpServletRequest request, OperationLog operationLog) {try {Map<String, Object> logData = new HashMap<>();logData.put("operation", operationLog.operation());logData.put("description", operationLog.description());logData.put("uri", request.getRequestURI());logData.put("method", request.getMethod());logData.put("ip", request.getRemoteAddr());logData.put("timestamp", System.currentTimeMillis());// 记录参数(如果启用)if (operationLog.logParams()) {Map<String, String[]> parameterMap = request.getParameterMap();if (!parameterMap.isEmpty()) {logData.put("parameters", parameterMap);}}logger.info("操作日志: {}", objectMapper.writeValueAsString(logData));} catch (Exception e) {logger.error("记录操作日志失败", e);}}
}

9.3 使用示例

@RestController
@RequestMapping("/api")
public class ApiController {@GetMapping("/public/info")@SkipLogin(reason = "公开测试接口")public Map<String, Object> publicEndpoint() {Map<String, Object> result = new HashMap<>();result.put("message", "这是公开接口,无需登录");result.put("timestamp", System.currentTimeMillis());return result;}@GetMapping("/private")public Map<String, Object> privateEndpoint() {Map<String, Object> result = new HashMap<>();result.put("message", "这是私有接口,需要登录");result.put("timestamp", System.currentTimeMillis());return result;}@PostMapping("/operation")@OperationLog(operation = "TEST_OPERATION", description = "测试操作", logParams = true)public Map<String, Object> testOperation(@RequestParam String param) {Map<String, Object> result = new HashMap<>();result.put("message", "操作执行成功");result.put("param", param);result.put("timestamp", System.currentTimeMillis());return result;}@GetMapping("/admin")@RequirePermission("ADMIN_ACCESS")public Map<String, Object> adminEndpoint() {Map<String, Object> result = new HashMap<>();result.put("message", "管理员接口");result.put("timestamp", System.currentTimeMillis());return result;}@GetMapping("/slow")public Map<String, Object> slowEndpoint() throws InterruptedException {// 模拟慢接口Thread.sleep(2000);Map<String, Object> result = new HashMap<>();result.put("message", "这是一个慢接口");result.put("timestamp", System.currentTimeMillis());return result;}@GetMapping("/error")public void errorEndpoint() {throw new RuntimeException("测试异常");}
}

10. 性能监控拦截器

package com.example.interceptor;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;/*** 性能监控拦截器* 统计接口的响应时间和调用次数*/
@Component
public class PerformanceInterceptor implements HandlerInterceptor {private static final Logger logger = LoggerFactory.getLogger(PerformanceInterceptor.class);// 存储每个接口的统计信息private final ConcurrentHashMap<String, ApiStats> apiStatsMap = new ConcurrentHashMap<>();@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 记录请求开始时间request.setAttribute("startTime", System.currentTimeMillis());return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {Long startTime = (Long) request.getAttribute("startTime");if (startTime == null) {return;}long endTime = System.currentTimeMillis();long responseTime = endTime - startTime;String uri = request.getRequestURI();String method = request.getMethod();String key = method + " " + uri;// 更新统计信息ApiStats stats = apiStatsMap.computeIfAbsent(key, k -> new ApiStats());stats.addRequest(responseTime);// 记录慢接口(超过1秒)if (responseTime > 1000) {logger.warn("慢接口告警 - {} 耗时: {} ms", key, responseTime);}// 定期输出统计信息(每100次请求)if (stats.getRequestCount() % 100 == 0) {logger.info("接口统计 - {}: 总请求数={}, 平均响应时间={} ms, 最大响应时间={} ms", key, stats.getRequestCount(), stats.getAverageResponseTime(), stats.getMaxResponseTime());}}/*** 获取所有接口的统计信息*/public ConcurrentHashMap<String, ApiStats> getAllStats() {return apiStatsMap;}/*** API统计信息类*/public static class ApiStats {private final LongAdder requestCount = new LongAdder();private final LongAdder totalResponseTime = new LongAdder();private final AtomicLong maxResponseTime = new AtomicLong(0);public void addRequest(long responseTime) {requestCount.increment();totalResponseTime.add(responseTime);// 更新最大响应时间long currentMax = maxResponseTime.get();while (responseTime > currentMax && !maxResponseTime.compareAndSet(currentMax, responseTime)) {currentMax = maxResponseTime.get();}}public long getRequestCount() {return requestCount.sum();}public long getAverageResponseTime() {long count = requestCount.sum();return count > 0 ? totalResponseTime.sum() / count : 0;}public long getMaxResponseTime() {return maxResponseTime.get();}public long getTotalResponseTime() {return totalResponseTime.sum();}}
}

11. 测试拦截器

11.1 创建测试控制器

package com.example.controller;import com.example.annotation.OperationLog;
import com.example.annotation.RequirePermission;
import com.example.annotation.SkipLogin;
import org.springframework.web.bind.annotation.*;import java.util.HashMap;
import java.util.Map;/*** 测试控制器 - 用于验证拦截器功能*/
@RestController
@RequestMapping("/test")
public class TestController {@GetMapping("/public")@SkipLogin(reason = "公开测试接口")public Map<String, Object> publicEndpoint() {Map<String, Object> result = new HashMap<>();result.put("message", "这是公开接口,无需登录");result.put("timestamp", System.currentTimeMillis());return result;}@GetMapping("/private")public Map<String, Object> privateEndpoint() {Map<String, Object> result = new HashMap<>();result.put("message", "这是私有接口,需要登录");result.put("timestamp", System.currentTimeMillis());return result;}@PostMapping("/operation")@OperationLog(operation = "TEST_OPERATION", description = "测试操作", logParams = true)public Map<String, Object> testOperation(@RequestParam String param) {Map<String, Object> result = new HashMap<>();result.put("message", "操作执行成功");result.put("param", param);result.put("timestamp", System.currentTimeMillis());return result;}@GetMapping("/admin")@RequirePermission("ADMIN_ACCESS")public Map<String, Object> adminEndpoint() {Map<String, Object> result = new HashMap<>();result.put("message", "管理员接口");result.put("timestamp", System.currentTimeMillis());return result;}@GetMapping("/slow")public Map<String, Object> slowEndpoint() throws InterruptedException {// 模拟慢接口Thread.sleep(2000);Map<String, Object> result = new HashMap<>();result.put("message", "这是一个慢接口");result.put("timestamp", System.currentTimeMillis());return result;}@GetMapping("/error")public void errorEndpoint() {throw new RuntimeException("测试异常");}
}

11.2 性能统计接口

package com.example.controller;import com.example.interceptor.PerformanceInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;/*** 监控控制器 - 提供系统监控信息*/
@RestController
@RequestMapping("/monitor")
public class MonitorController {@Autowiredprivate PerformanceInterceptor performanceInterceptor;@GetMapping("/stats")public Map<String, Object> getApiStats() {Map<String, Object> result = new HashMap<>();ConcurrentHashMap<String, PerformanceInterceptor.ApiStats> allStats = performanceInterceptor.getAllStats();Map<String, Map<String, Object>> formattedStats = new HashMap<>();allStats.forEach((api, stats) -> {Map<String, Object> statMap = new HashMap<>();statMap.put("requestCount", stats.getRequestCount());statMap.put("averageResponseTime", stats.getAverageResponseTime());statMap.put("maxResponseTime", stats.getMaxResponseTime());statMap.put("totalResponseTime", stats.getTotalResponseTime());formattedStats.put(api, statMap);});result.put("apiStats", formattedStats);result.put("timestamp", System.currentTimeMillis());return result;}
}

11.3 测试拦截器功能

启动应用后,可以通过以下方式测试拦截器:

  1. 测试公开接口(跳过登录)

    curl -X GET "http://localhost:8080/test/public"
    
  2. 测试私有接口(需要登录)

    # 未登录时会返回401
    curl -X GET "http://localhost:8080/test/private"
    
  3. 测试操作日志

    curl -X POST "http://localhost:8080/test/operation?param=testValue"
    
  4. 测试性能监控

    # 访问慢接口
    curl -X GET "http://localhost:8080/test/slow"# 查看统计信息
    curl -X GET "http://localhost:8080/monitor/stats"
    
  5. 测试限流功能

    # 快速发送多个请求测试限流
    for i in {1..105}; docurl -X GET "http://localhost:8080/test/private"
    done
    

12. 拦截器与Spring Security集成

12.1 与Spring Security协同工作

package com.example.interceptor;import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** Spring Security集成拦截器* 在Spring Security基础上添加额外的安全检查*/
@Component
public class SecurityEnhancementInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 获取当前认证信息Authentication authentication = SecurityContextHolder.getContext().getAuthentication();if (authentication != null && authentication.isAuthenticated()) {String username = authentication.getName();// 检查用户状态(例如:是否被禁用、是否需要重新验证等)if (isUserDisabled(username)) {response.setStatus(HttpServletResponse.SC_FORBIDDEN);response.setContentType("application/json;charset=UTF-8");response.getWriter().write("{\"code\":403,\"message\":\"用户已被禁用\"}");return false;}// 检查密码是否即将过期if (isPasswordExpiringSoon(username)) {response.setHeader("X-Password-Warning", "密码即将过期,请及时修改");}// 记录用户活动logUserActivity(username, request);}return true;}private boolean isUserDisabled(String username) {// 检查用户是否被禁用的逻辑return false;}private boolean isPasswordExpiringSoon(String username) {// 检查密码是否即将过期的逻辑return false;}private void logUserActivity(String username, HttpServletRequest request) {// 记录用户活动的逻辑}
}

13. 拦截器配置的最佳实践

13.1 环境相关的拦截器配置

package com.example.config;import com.example.interceptor.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*** 环境相关的拦截器配置* 根据不同环境启用不同的拦截器*/
@Configuration
public class EnvironmentSpecificWebConfig implements WebMvcConfigurer {@Autowiredprivate Environment environment;@Autowiredprivate LogInterceptor logInterceptor;@Autowiredprivate PerformanceInterceptor performanceInterceptor;@Autowiredprivate LoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 所有环境都启用的拦截器registry.addInterceptor(logInterceptor).addPathPatterns("/**").order(1);// 只在生产环境启用的拦截器if (isProdEnvironment()) {registry.addInterceptor(new RateLimitInterceptor()).addPathPatterns("/api/**").order(2);}// 只在开发和测试环境启用的拦截器if (isDevOrTestEnvironment()) {registry.addInterceptor(performanceInterceptor).addPathPatterns("/**").order(3);}// 根据配置决定是否启用登录拦截器if (isLoginRequired()) {registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns("/public/**", "/login", "/register").order(4);}}private boolean isProdEnvironment() {String[] activeProfiles = environment.getActiveProfiles();for (String profile : activeProfiles) {if ("prod".equals(profile) || "production".equals(profile)) {return true;}}return false;}private boolean isDevOrTestEnvironment() {String[] activeProfiles = environment.getActiveProfiles();for (String profile : activeProfiles) {if ("dev".equals(profile) || "test".equals(profile)) {return true;}}return false;}private boolean isLoginRequired() {return environment.getProperty("app.security.login-required", Boolean.class, true);}
}

13.2 拦截器配置外部化

application.yml中配置拦截器参数:

app:interceptor:# 日志拦截器配置log:enabled: truelog-parameters: truelog-response: false# 限流拦截器配置  rate-limit:enabled: truemax-requests: 100time-window: 60000# 性能监控配置performance:enabled: trueslow-threshold: 1000stats-interval: 100security:login-required: trueexclude-paths:- /public/**- /static/**- /login- /register

对应的配置类:

package com.example.config;import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;import java.util.List;/*** 拦截器配置属性*/
@Component
@ConfigurationProperties(prefix = "app.interceptor")
public class InterceptorProperties {private LogConfig log = new LogConfig();private RateLimitConfig rateLimit = new RateLimitConfig();private PerformanceConfig performance = new PerformanceConfig();// getter和setter方法public static class LogConfig {private boolean enabled = true;private boolean logParameters = true;private boolean logResponse = false;// getter和setter方法}public static class RateLimitConfig {private boolean enabled = true;private int maxRequests = 100;private long timeWindow = 60000;// getter和setter方法}public static class PerformanceConfig {private boolean enabled = true;private long slowThreshold = 1000;private int statsInterval = 100;// getter和setter方法}
}

14. 常见问题和解决方案

14.1 拦截器不生效的问题

问题1:拦截器没有被扫描到

解决方案:确保拦截器类上有@Component注解,并且在Spring的扫描范围内

@Component  // 必须添加这个注解
public class MyInterceptor implements HandlerInterceptor {// ...
}

问题2:拦截器没有注册

解决方案:确保在WebMvcConfigurer中正确注册了拦截器

@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate MyInterceptor myInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(myInterceptor)  // 确保调用了这个方法.addPathPatterns("/**");}
}

问题3:路径配置错误

解决方案:检查路径匹配模式是否正确

// 正确的路径配置
registry.addInterceptor(interceptor).addPathPatterns("/api/**")      // 拦截/api/下的所有路径.excludePathPatterns("/api/public/**");  // 排除公共API

14.2 拦截器执行顺序问题

问题:拦截器执行顺序不正确

解决方案:使用order()方法明确指定执行顺序

@Override
public void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(firstInterceptor).order(1);   // 先执行registry.addInterceptor(secondInterceptor).order(2);  // 后执行
}

14.3 静态资源被拦截的问题

问题:拦截器拦截了静态资源请求

解决方案:在拦截器配置中排除静态资源路径

registry.addInterceptor(interceptor).addPathPatterns("/**").excludePathPatterns("/static/**","/css/**", "/js/**","/images/**","/favicon.ico");

14.4 无法获取请求体的问题

问题:在拦截器中无法获取POST请求的请求体

解决方案:使用ContentCachingRequestWrapper包装请求

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 包装请求以便可以多次读取请求体if (!(request instanceof ContentCachingRequestWrapper)) {request = new ContentCachingRequestWrapper(request);}// 现在可以读取请求体了// ...return true;
}

14.5 响应已提交的问题

问题:尝试修改已提交的响应

解决方案:在修改响应前检查是否已提交

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {if (!response.isCommitted()) {  // 检查响应是否已提交// 可以安全地修改响应response.setHeader("Custom-Header", "Value");}
}

15. 性能优化建议

15.1 拦截器性能优化

  1. 避免在拦截器中执行耗时操作
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// ❌ 错误:在拦截器中执行数据库查询// User user = userService.findByUsername(username);// ✅ 正确:使用缓存或异步处理User user = userCache.get(username);if (user == null) {// 异步加载用户信息asyncUserLoader.loadUser(username);}return true;
}
  1. 合理使用拦截器缓存
@Component
public class CachedInterceptor implements HandlerInterceptor {private final Cache<String, Object> cache = Caffeine.newBuilder().maximumSize(1000).expireAfterWrite(5, TimeUnit.MINUTES).build();@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String cacheKey = generateCacheKey(request);Object cachedResult = cache.getIfPresent(cacheKey);if (cachedResult != null) {// 使用缓存结果return (Boolean) cachedResult;}// 执行实际逻辑boolean result = doActualCheck(request);cache.put(cacheKey, result);return result;}
}
  1. 减少不必要的拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {// 只对需要的路径应用拦截器registry.addInterceptor(expensiveInterceptor).addPathPatterns("/api/admin/**")  // 只拦截管理员API.excludePathPatterns("/api/admin/public/**");
}

16. 拦截器单元测试

16.1 拦截器单元测试示例

package com.example.interceptor;import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;/*** 拦截器单元测试*/
@ExtendWith(MockitoExtension.class)
class LoginInterceptorTest {private LoginInterceptor loginInterceptor;@Mockprivate Object handler;private MockHttpServletRequest request;private MockHttpServletResponse response;@BeforeEachvoid setUp() {loginInterceptor = new LoginInterceptor();request = new MockHttpServletRequest();response = new MockHttpServletResponse();}@Testvoid testPreHandle_UserLoggedIn_ShouldReturnTrue() throws Exception {// 准备测试数据request.getSession().setAttribute("user", new Object());// 执行测试boolean result = loginInterceptor.preHandle(request, response, handler);// 验证结果assertTrue(result);assertEquals(200, response.getStatus());}@Testvoid testPreHandle_UserNotLoggedIn_ShouldReturnFalse() throws Exception {// 执行测试boolean result = loginInterceptor.preHandle(request, response, handler);// 验证结果assertFalse(result);assertEquals(401, response.getStatus());}@Testvoid testPreHandle_AjaxRequest_ShouldReturnJsonResponse() throws Exception {// 准备AJAX请求request.addHeader("X-Requested-With", "XMLHttpRequest");// 执行测试boolean result = loginInterceptor.preHandle(request, response, handler);// 验证结果assertFalse(result);assertEquals(401, response.getStatus());assertEquals("application/json;charset=UTF-8", response.getContentType());assertTrue(response.getContentAsString().contains("请先登录"));}
}

16.2 集成测试

package com.example.integration;import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureWebMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;/*** 拦截器集成测试*/
@SpringBootTest
@AutoConfigureWebMvc
class InterceptorIntegrationTest {@Autowiredprivate MockMvc mockMvc;@Testvoid testPublicEndpoint_ShouldBeAccessible() throws Exception {mockMvc.perform(get("/test/public")).andExpect(status().isOk()).andExpect(jsonPath("$.message").value("这是公开接口,无需登录"));}@Testvoid testPrivateEndpoint_WithoutLogin_ShouldReturn401() throws Exception {mockMvc.perform(get("/test/private")).andExpect(status().isUnauthorized());}@Testvoid testPrivateEndpoint_WithLogin_ShouldBeAccessible() throws Exception {mockMvc.perform(get("/test/private").sessionAttr("user", new Object())).andExpect(status().isOk());}
}

17. 最佳实践总结

17.1 设计原则

  1. 单一职责:每个拦截器只负责一个特定功能
  2. 性能优先:避免在拦截器中执行耗时操作
  3. 异常安全:妥善处理异常,避免影响正常请求流程
  4. 可配置性:通过配置文件控制拦截器行为

17.2 代码组织

src/main/java/com/example/
├── interceptor/           # 拦截器包
│   ├── LogInterceptor.java
│   ├── LoginInterceptor.java
│   ├── PermissionInterceptor.java
│   └── PerformanceInterceptor.java
├── annotation/            # 自定义注解
│   ├── SkipLogin.java
│   ├── RequirePermission.java
│   └── OperationLog.java
├── config/               # 配置类
│   ├── WebConfig.java
│   └── InterceptorProperties.java
└── Application.java      # 主启动类

17.3 命名规范

  • 拦截器类名以Interceptor结尾
  • 注解类名使用动词形式,如SkipLoginRequirePermission
  • 配置类名以Config结尾

17.4 监控和调试

  1. 添加详细的日志记录
  2. 使用Spring Boot Actuator监控拦截器性能
  3. 在开发环境启用调试拦截器
  4. 定期检查拦截器的性能统计

18. 总结

SpringBoot拦截器是Web应用开发中的重要组件,通过本教程你应该已经掌握了:

18.1 核心知识点

  1. 拦截器基础概念:理解拦截器的工作原理和生命周期
  2. 三个核心方法:preHandle、postHandle、afterCompletion的使用
  3. 实现方式:通过HandlerInterceptor接口创建自定义拦截器
  4. 注册配置:使用WebMvcConfigurer注册和配置拦截器
  5. 路径匹配:灵活配置拦截和排除路径

18.2 实际应用场景

  1. 登录验证:检查用户是否已登录
  2. 权限控制:基于角色和权限的访问控制
  3. 日志记录:记录请求和响应信息
  4. 性能监控:统计接口响应时间和调用次数
  5. 限流控制:防止接口被恶意调用
  6. 跨域处理:处理CORS跨域请求

18.3 高级特性

  1. 与注解结合:创建更灵活的拦截逻辑
  2. 多拦截器协作:合理配置执行顺序
  3. 异常处理:统一处理拦截器中的异常
  4. 性能优化:使用缓存和异步处理提高性能

18.4 学习建议

  • 从简单开始:先实现基础的日志拦截器
  • 逐步增加功能:添加登录验证、权限控制等
  • 注意性能:避免在拦截器中执行耗时操作
  • 充分测试:编写单元测试和集成测试
  • 生产验证:在生产环境中验证拦截器的稳定性

通过合理使用拦截器,可以让你的SpringBoot应用更加安全、高效和易于维护!拦截器是实现横切关注点的重要工具,掌握它们对于构建企业级应用至关重要。

http://www.xdnf.cn/news/14949.html

相关文章:

  • 玄机——某学校系统中挖矿病毒应急排查
  • 人脸活体识别3:C/C++实现人脸眨眼 张嘴 点头 摇头识别(可实时检测)
  • lerobot 工程笔记(一)——使用smolvla控制so101
  • 【AI落地应用实战】AIGC赋能职场PPT汇报:从效率工具到辅助优化
  • Docker Compose 基础——AI教你学Docker
  • 链表的实现
  • Flink-1.19.0源码详解5-JobGraph生成-前篇
  • Node.js-http模块
  • Appium 简介
  • 大语言模型:是逐字生成还是一次多词?
  • GO Web 框架 Gin 完全解析与实践
  • 【NLP第二期中文分词技术:规则、统计与混合方法全解】
  • 笨方法学python-习题12
  • Disruptor架构哲学
  • 结构体实战:用Rust编写矩形面积计算器
  • MySQL 中 InnoDB 存储引擎与 MyISAM 存储引擎的区别是什么?
  • 8.Docker镜像讲解
  • 阿里巴巴 Qwen 系列大模型发展时间线与主要特性
  • 数字图像处理学习笔记
  • C#.Net筑基-优雅LINQ的查询艺术
  • 算法-每日一题(DAY12)最长和谐子序列
  • TypeScript 安装使用教程
  • 电子面单系统开发全解析
  • 深度学习进阶:自然语言处理的推荐点评
  • 杭州来未来科技 Java 实习面经
  • STM32——代码开发顺序
  • 模型部署与推理--利用python版本onnxruntime模型部署与推理
  • 【仿muduo库实现并发服务器】Acceptor模块
  • 微信小程序21~30
  • grom使用mysql快速上手