springboot 拦截器(超详细)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观

前言

在开发 Web 应用时,我们常常需要在请求到达控制器(Controller)之前或响应返回客户端之后,执行一些通用逻辑,例如登录验证、日志记录、性能监控或权限控制。此时,Spring Boot 拦截器(Interceptor)便成为了一个不可或缺的工具。它如同一座桥梁,允许开发者在请求处理的各个阶段插入自定义逻辑,从而提升代码的复用性和系统的可维护性。

本文将从拦截器的基础概念出发,结合实际案例和代码示例,深入讲解其工作原理、实现步骤及应用场景。无论是编程新手还是有一定经验的开发者,都能从中找到适合自己的学习路径。


一、拦截器是什么?它与过滤器有何不同?

1. 拦截器的定义

Spring Boot 拦截器是 Spring 框架提供的、基于 Java 的 AOP(面向切面编程)机制实现的一种请求处理组件。它允许开发者在请求被路由到具体的控制器方法之前(或之后)执行自定义逻辑。

形象比喻
可以将拦截器想象成一座写字楼的“安检门”。当用户(请求)进入大楼(应用)时,安检门会检查其携带的物品(请求头、参数等),并根据规则决定是否放行。如果用户携带了违禁品(例如未登录用户尝试访问敏感接口),安检门会直接拦截,而无需进入办公区域(控制器方法)。

2. 拦截器与过滤器的区别

在 Web 开发中,拦截器过滤器(Filter)都用于处理请求的预处理或后处理,但它们的工作阶段和适用场景不同:

对比维度拦截器(Interceptor)过滤器(Filter)
工作阶段基于 Spring MVC 的处理流程,在控制器方法调用前/后执行基于 Servlet 规范,在更底层的 HTTP 请求处理阶段执行
功能范围仅对 Spring MVC 处理的请求生效对所有通过 Servlet 容器的请求生效
编程语言纯 Java 代码实现,依赖 Spring 框架可基于 Java 或其他语言(如 Groovy)实现
适用场景需要与 Spring 的业务逻辑深度集成的场景需要跨框架或非 Spring 控制的通用请求处理

总结
如果需求仅涉及 Spring MVC 的请求处理流程,优先选择拦截器;若需要处理非 Spring 控制的请求(如静态资源、非 Spring 管理的接口),则使用过滤器更合适。


二、拦截器的核心原理与执行流程

1. 拦截器的生命周期

一个典型的拦截器需要实现 HandlerInterceptor 接口,并重写以下三个方法:

  • preHandle():在控制器方法执行之前调用,决定是否放行请求。
  • postHandle():在控制器方法执行之后、视图渲染之前调用,可用于后置处理(如记录日志)。
  • afterCompletion():在整个请求处理流程完成后调用,通常用于资源释放或最终记录。

2. 执行流程图解

客户端请求 → 过滤器(Filter) → 拦截器(preHandle) → 控制器方法 → 拦截器(postHandle) → 视图渲染 → 拦截器(afterCompletion)  

3. 拦截器链的顺序

当配置多个拦截器时,它们的执行顺序遵循以下规则:

  • preHandle() 方法按配置顺序依次执行
  • postHandle() 方法按逆序执行(与配置顺序相反);
  • afterCompletion() 方法同样按逆序执行

示例
假设配置了拦截器 A 和拦截器 B,则执行顺序为:

preHandle(A) → preHandle(B) → 控制器方法 → postHandle(B) → postHandle(A) → afterCompletion(B) → afterCompletion(A)  

三、如何实现一个简单的拦截器?

1. 步骤一:创建拦截器类

首先,创建一个实现 HandlerInterceptor 接口的类,并重写所需方法。

import org.springframework.stereotype.Component;  
import org.springframework.web.servlet.HandlerInterceptor;  
import javax.servlet.http.HttpServletRequest;  
import javax.servlet.http.HttpServletResponse;  

@Component  
public class LoginInterceptor implements HandlerInterceptor {  
    // 在控制器方法执行前拦截请求  
    @Override  
    public boolean preHandle(HttpServletRequest request,  
                            HttpServletResponse response,  
                            Object handler) throws Exception {  
        // 检查用户是否登录(例如从 Session 中获取用户信息)  
        Object user = request.getSession().getAttribute("user");  
        if (user == null) {  
            // 未登录,返回 401 状态码  
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);  
            return false; // 拦截请求,不继续执行  
        }  
        return true; // 放行请求  
    }  
}  

2. 步骤二:注册拦截器

通过实现 WebMvcConfigurer 接口,将拦截器添加到 Spring MVC 的拦截器链中。

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;  

@Configuration  
public class WebConfig implements WebMvcConfigurer {  
    @Autowired  
    private LoginInterceptor loginInterceptor;  

    @Override  
    public void addInterceptors(InterceptorRegistry registry) {  
        // 添加拦截器,并指定拦截路径  
        registry.addInterceptor(loginInterceptor)  
                .addPathPatterns("/api/**") // 拦截所有以 /api/ 开头的请求  
                .excludePathPatterns("/api/public/**"); // 排除 /api/public/** 的路径  
    }  
}  

3. 步骤三:测试拦截器

通过发送一个未登录的请求到 /api/user,预期返回 401 Unauthorized 状态码:

curl -X GET http://localhost:8080/api/user  

四、实战案例:拦截器的常见应用场景

1. 场景一:登录验证拦截器

需求:确保用户访问 /api/private 接口时已登录。

实现逻辑

  • preHandle() 中检查 Session 中是否存在用户信息;
  • 若未登录,直接返回 401 状态码并终止请求。

代码示例

// 在 LoginInterceptor 类中实现  
@Override  
public boolean preHandle(HttpServletRequest request,  
                        HttpServletResponse response,  
                        Object handler) {  
    if (request.getRequestURI().contains("/api/private")) {  
        // 针对特定路径增强校验  
        // ...  
    }  
    return super.preHandle(request, response, handler);  
}  

2. 场景二:日志拦截器

需求:记录每个请求的耗时和响应状态码。

实现逻辑

  • preHandle() 中记录请求开始时间;
  • afterCompletion() 中计算耗时并输出日志。
public class LoggingInterceptor implements HandlerInterceptor {  
    private static final Logger logger = LoggerFactory.getLogger(LoggingInterceptor.class);  

    @Override  
    public boolean preHandle(HttpServletRequest request,  
                            HttpServletResponse response,  
                            Object handler) {  
        request.setAttribute("startTime", System.currentTimeMillis());  
        return true;  
    }  

    @Override  
    public void afterCompletion(HttpServletRequest request,  
                               HttpServletResponse response,  
                               Object handler,  
                               Exception ex) throws Exception {  
        long startTime = (Long) request.getAttribute("startTime");  
        long duration = System.currentTimeMillis() - startTime;  
        logger.info("Request: {} took {} ms, Status: {}",  
                request.getRequestURI(), duration, response.getStatus());  
    }  
}  

3. 场景三:权限控制拦截器

需求:根据用户角色限制对 /api/admin 接口的访问。

实现逻辑

  • preHandle() 中检查用户角色是否为 ADMIN
  • 若角色不符,返回 403 Forbidden 状态码。
public class RoleInterceptor implements HandlerInterceptor {  
    @Override  
    public boolean preHandle(HttpServletRequest request,  
                            HttpServletResponse response,  
                            Object handler) {  
        String role = (String) request.getSession().getAttribute("role");  
        if (!"ADMIN".equals(role)) {  
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);  
            return false;  
        }  
        return true;  
    }  
}  

五、高级技巧与常见问题

1. 拦截器链的顺序管理

通过调整 addInterceptors() 方法中拦截器的添加顺序,可以控制拦截器的执行优先级。例如:

registry.addInterceptor(interceptorA).addPathPatterns("/**");  
registry.addInterceptor(interceptorB).addPathPatterns("/**");  
// interceptorA 的 preHandle() 会先于 interceptorB 执行  

2. 如何处理跨域请求?

拦截器本身不直接处理跨域问题,但可以通过以下方式结合使用:

  • 在拦截器中设置响应头(如 Access-Control-Allow-Origin);
  • 或者使用过滤器(Filter)统一处理跨域请求。

3. 拦截器与异常处理

若拦截器内部抛出异常,可以通过 Spring 的全局异常处理器(@ControllerAdvice)捕获并处理,避免直接暴露技术细节。

4. 性能优化建议

  • 避免在拦截器中执行耗时操作(如复杂的数据库查询),以免影响请求响应速度;
  • 使用缓存或静态配置优化拦截逻辑(如路径匹配规则)。

六、总结与展望

通过本文的学习,我们掌握了 Spring Boot 拦截器的核心概念、实现方法及典型应用场景。拦截器不仅简化了重复代码的编写,还为系统提供了灵活的扩展能力。无论是登录验证、日志记录还是权限控制,拦截器都能帮助开发者高效实现这些需求。

随着项目规模的扩大,合理设计拦截器链和管理拦截逻辑的优先级将成为关键。未来,结合 Spring AOP 或其他框架(如 Feign Client 的拦截器),拦截器的使用场景将进一步扩展。希望读者能通过实践,将拦截器的威力充分融入到自己的项目中!


通过本文的学习,读者可以快速掌握 Spring Boot 拦截器的开发与应用技巧,为构建更健壮、可维护的 Web 应用奠定坚实基础。

最新发布