Spring Boot拦截器的用法(非常详细)
Spring MVC 拦截器作为一个附加功能,其重要性远不如 struts 中的拦截器。Spring MVC 拦截器主要是帮我们按照一定规则拦截请求,类似于 Servlet 开发过程中使用的过滤器 Filter,拦截器主要是针对某个业务功能进行预处理和后处理。
拦截器的执行流程如下图所示:
拦截器的本质是 AOP 面向切面编程,也就是说符合横切关注点的功能都可以考虑使用拦截器实现。拦截器有以下几种常见应用场景:
自定义拦截器,参考代码如下:
HandlerInterceptor 接口中的三个方法说明如下:
1) preHandle()。Controller 接口方法执行前用于拦截请求的方法:
2) postHandle()。在 Controller 中的接口方法执行后、JSP 视图执行前执行:
3) afterCompletion()。在 JSP 执行后执行,不能通过 HttpServletRequest 或 HttpServletResponse 跳转页面。
拦截器配置类,参考代码如下:
Controller 接口测试类,参考代码如下:
Spring Boot 为我们提供了非常简便的方式,即在配置类中可以通过注册拦截器的顺序来自定义拦截器的执行顺序,详细实现说明如下。
1) 新增拦截器,参考代码如下:
2) 修改配置类 InterceptorConfig.java。在配置类中注册拦截器 SecondInterceptor,并且指定先执行拦截器 LoginInterceptor 再执行拦截器 SecondInterceptor。参考代码如下:
对于多个拦截器,正常流程下拦截器方法的执行顺序如下图所示:
非正常情况下,第一个拦截器在 preHandle() 处放行,第二个拦截器在 postHandle() 处被拦截。
修改第一个拦截器 LoginInterceptor.java 的代码,参考代码如下:
修改第二个拦截器 SecondInterceptor.java 的代码,参考代码如下:
启动项目,在浏览器中访问路径 http://localhost:8080/interceptor/test。两个拦截器的执行结果如下图所示:
非正常情况下的拦截器(第一个拦截器在 preHandle() 处被拦截,在第二个拦截器处放行)。
修改第一个拦截器 LoginInterceptor.java 的代码,参考代码如下:
修改第二个拦截器 SecondInterceptor.java 的代码,参考代码如下:
对比以上两种非正常情况下执行的拦截器,我们可以得出三点结论:
拦截器的执行流程如下图所示:

拦截器的本质是 AOP 面向切面编程,也就是说符合横切关注点的功能都可以考虑使用拦截器实现。拦截器有以下几种常见应用场景:
- 权限检查。例如,用户登录检测,访问项目内部接口时,可以通过拦截器检测用户是否登录,如果未登录,则直接返回用户登录页面。
- 日志记录。拦截器可以用来连接一些重要的请求,从而记录这些请求的重要信息、仪表与信息的监控、信息统计以及page view的计算等。
- 性能监控。可以记录某个请求在通过拦截器进入处理器的开始时间和在处理器处理完之后记录结束时间,从而得到该请求的耗时,可以用于性能的监控,准确定位到哪些功能卡顿,便于日后进行功能的调优。
- 通用行为。拦截器还可以读取 cookie、获取用户信息,并将用户信息存放在请求中,方便后续业务流程使用。
自定义拦截器
在 Spring Boot 项目中自定义拦截器有两个关键步骤:- 一是创建一个拦截器,实现 HandlerInterceptor 接口,并且按照自身需求重写接口中的方法;
- 二是创建一个拦截器的配置类,实现 WebMvcConfigurer.java,并且重写 addResourceHandlers() 方法,定义拦截规则。
自定义拦截器,参考代码如下:
/** * 登录拦截器 * Spring MVC 拦截器作为一个附加功能,重要性远不及 struts 中的拦截器 * 拦截器由 dispatcherServlet 调用,而 JSP 不穿过 dispatcherServlet,所以拦截器不会拦截 JSP */ public class LoginInterceptor implements HandlerInterceptor { /** * 在请求到达具体的 Controller 方法之前执行,且当返回 true 时才执行 Controller 和另外两个方法 */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("LoginInterceptor-执行了 preHandle() 方法!"); // 为测试不拦截请求设置的测试数据 request.getSession().setAttribute("username", "bobo"); String username = (String) request.getSession().getAttribute("username"); if (username == null) { response.sendRedirect("/html/login.html"); return false; } return true; } /** * 在执行完 Controller 具体方法之后、返回页面之前执行,而且此时还可以修改 ModelAndView */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("LoginInterceptor-执行了 postHandle() 方法!"); } /** * 在即将到达页面之前执行,此时不可以修改 ModelAndView */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("LoginInterceptor-执行了 afterCompletion() 方法!"); } }
HandlerInterceptor 接口中的三个方法说明如下:
1) preHandle()。Controller 接口方法执行前用于拦截请求的方法:
- 可以使用 HttpServletRequest 或 HttpServletResponse 跳转到指定页面。
- 该方法返回 true 放行,执行下一个拦截器,如果没有拦截器,就执行请求 Controller 的接口方法。
- 该方法返回 false 不放行,不会执行 Controller 中的方法。
2) postHandle()。在 Controller 中的接口方法执行后、JSP 视图执行前执行:
- 可以使用 HttpServletRequest 或 HttpServletResponse 跳转到指定页面。
- 如果指定了跳转的页面,那么 Controller 中方法跳转的页面就不生效了。
3) afterCompletion()。在 JSP 执行后执行,不能通过 HttpServletRequest 或 HttpServletResponse 跳转页面。
拦截器配置类,参考代码如下:
/** * 拦截器配置类 */ @Configuration public class InterceptorConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { // 注册拦截器 LoginInterceptor registry.addInterceptor(new LoginInterceptor()) // 拦截所有路径 .addPathPatterns("/**") // 添加不被拦截的路径、登录路径和静态资源路径 .excludePathPatterns("/html/login.html") .excludePathPatterns("/css/**"); } }
Controller 接口测试类,参考代码如下:
/** * 拦截器接口测试类 */ @RestController @RequestMapping("/interceptor") public class InterceptorController { @GetMapping("/test") public String testInterceptor() { return "hello interceptor"; } }启动项目,访问路径 http://localhost:8080/interceptor/test,控制台拦截器的执行效果如下图所示:

多拦截器配置
在实际开发过程中,我们可能会需要配置多个拦截器,此时我们首先会考虑到两个问题:- 第一,应该怎么注册多个拦截器;
- 第二,怎么自定义拦截器的执行顺序。
Spring Boot 为我们提供了非常简便的方式,即在配置类中可以通过注册拦截器的顺序来自定义拦截器的执行顺序,详细实现说明如下。
1) 新增拦截器,参考代码如下:
/** * 拦截器(用于测试拦截器执行顺序) */ public class SecondInterceptor implements HandlerInterceptor { /** * 在请求到达具体的 Controller 方法之前执行,且当返回 true 时才执行 Controller 和另外两个方法 */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("SecondInterceptor-执行了 preHandle() 方法!"); return true; } /** * 在执行完 Controller 具体方法之后、返回页面之前执行,而且还可以修改 ModelAndView */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("SecondInterceptor-执行了 postHandle() 方法!"); } /** * 在即将到达页面之前执行,此时不可以修改 ModelAndView */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("SecondInterceptor-执行了 afterCompletion() 方法!"); } }
2) 修改配置类 InterceptorConfig.java。在配置类中注册拦截器 SecondInterceptor,并且指定先执行拦截器 LoginInterceptor 再执行拦截器 SecondInterceptor。参考代码如下:
/** * 拦截器配置类 */ @Configuration public class InterceptorConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { /* 可以通过 registry.addInterceptor() 方法控制拦截器注册顺序, 从而达到自定义拦截器顺序的目的 */ // 先注册拦截器 LoginInterceptor registry.addInterceptor(new LoginInterceptor()) // 拦截所有路径 .addPathPatterns("/**") // 添加不被拦截的路径、登录路径和静态资源路径 .excludePathPatterns("/html/login.html") .excludePathPatterns("/css/**"); // 再注册拦截器 SecondInterceptor() registry.addInterceptor(new SecondInterceptor()) // 拦截所有路径 .addPathPatterns("/**") // 添加不被拦截的路径、登录路径和静态资源路径 .excludePathPatterns("/html/login.html") .excludePathPatterns("/js/**") .excludePathPatterns("/img/**"); } }启动项目,在浏览器中访问路径 http://localhost:8080/interceptor/test。两个拦截器的执行结果如下图所示:

对于多个拦截器,正常流程下拦截器方法的执行顺序如下图所示:

非正常情况下,第一个拦截器在 preHandle() 处放行,第二个拦截器在 postHandle() 处被拦截。
修改第一个拦截器 LoginInterceptor.java 的代码,参考代码如下:
public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("LoginInterceptor-执行了 preHandle() 方法!"); return true; } }
修改第二个拦截器 SecondInterceptor.java 的代码,参考代码如下:
public class SecondInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("SecondInterceptor-执行了 preHandle() 方法!"); return false; } }
启动项目,在浏览器中访问路径 http://localhost:8080/interceptor/test。两个拦截器的执行结果如下图所示:

非正常情况下的拦截器(第一个拦截器在 preHandle() 处被拦截,在第二个拦截器处放行)。
修改第一个拦截器 LoginInterceptor.java 的代码,参考代码如下:
public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("LoginInterceptor-执行了 preHandle() 方法!"); return false; } }
修改第二个拦截器 SecondInterceptor.java 的代码,参考代码如下:
public class SecondInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("SecondInterceptor-执行了 preHandle() 方法!"); return true; } }启动项目,在浏览器中访问路径 http://localhost:8080/interceptor/test。两个拦截器的执行结果如下图所示:

对比以上两种非正常情况下执行的拦截器,我们可以得出三点结论:
- 拦截器 1 放行,其后紧接着的拦截器 2 的 preHandle() 方法才会执行;
- 如果拦截器 1 放行,拦截器 2 的preHandle()方法不放行,那么拦截器 2 的 postHandle() 和 afterCompletion() 方法将不会执行;
- 只要存在一个拦截器放行,那么此拦截器后的所有拦截器 preHandle() 方法均不会被执行。