Spring Mvc中拦截器Interceptor用法解读
目录
- 一、概述
- 二、拦截器和过滤器之间的区别
- 三、自定义实现拦截器
- 四、多个拦截器的执行顺序
- 总结
一、概述
拦截器常用于在请求处理的不同阶段插入自定义逻辑。
Spring MVC的拦截器作用是在请求到达控制器之前或之后进行拦截,可以对请求和响应进行一些特定的处理。
如:
- 登录验证:对于需要登录才能访问的网址,使用拦截器可以判断用户是否已登录,如果未登录则跳转到登录页面。
- 权限校验:根据用户权限对部分网址进行访问控制,拒绝未经授权的用户访问。
- 请求日志:记录请求信息,例如请求地址、请求参数、请求时间等,用于排查问题和性能优化。
- 更改响应:可以对响应的内容进行修改,例如添加头信息、调整响应内容格式等。
二、拦截器和过滤器之间的区别
关于过滤器可以看我之前的文章过滤器Filter的介绍和使用。
我们很容易发现拦截器和过滤器十分相似,他们都是对某一阶段的前后进行拦截,进行一些处理。那么他们之间有什么不同呢?
过滤器(Filter)是servlet中定义的,而拦截器(HandlerInterceptor)则是由Spring MVC框架提供
二者所作用的范围不同
- 过滤器更注重在**请求和响应(即在Servlet之前)**的流程中进行处理,可以修改请求和响应的内容,例如设置编码和字符集、请求头、状态码等。
- 拦截器则更加侧重于对控制器进行前置或后置处理,在请求到达控制器之前或之后进行特定的操作,例如打印日志、权限验证等。
三、自定义实现拦截器
Spring MVC 提供了 HandlerInterceptor
接口,开发者可以通过实现这个接口来创建自定义的拦截器。
其中定义了三个默认方法,用于对不同阶段进行拦截:
preHandle(HttpServletRequest request, HttpServletResponse response, Object handler):
- 在控制器方法执行前调用。
- 返回
true
表示继续执行后续的拦截器和控制器方法;返回false
表示中断执行,不再调用后续的拦截器和控制器方法。 - 可以用于权限验证、日志记录等。
postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView):
- 在控制器方法执行后,但在视图渲染前调用。
- 可以用于修改
ModelAndView
对象,添加额外的数据等。
afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex):js
- 在整个请求python处理完成后调用,无论是否发生异常。
- 可以用于资源清理、日志记录等。
创建自定义拦截器:
import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import Javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MyInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 在控制器方法执行前调用 System.out.println("preHandle..." ); //这里我们直接返回true return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { lpeAk // 在控制器方法执行后,但在视图渲染前调用 System.out.println("postHandle..."); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // 在整个请求处理完成后调用 System.out.println("AfterCompletion"); } }
此时要想要该拦截器生效,我们还需在spring mvc配置文件中进行配置(默认对所有控制器进行拦截)
<mvc:interceptors> <mvc:interceptor> <!--设置要拦截的路径--> <mvc:mapping path="/**"/> <!--指定不进行拦截的路径--> <mvc:exclude-mapping path="/test"/> <!--配置自定义拦截器的类路径--> <bean class="com.example.MyInterceptor"/> </mvc:interceptor> </mvc:interceptors>
也可以通过在自定义 拦截器的类上加上@component注解,此时的配置文件为
<mvc:interceptors> <mvc:interceptor> <!--设置要拦截的路径--> <mvc:mapping path="/**"/> <!--指定不进行拦截的路径--> <mvc:exclude-mapping path="/test"/> <!--默认名字为类名首字母小写--> <ref bean="myInterceptor"></ref> </mvc:interceptor> </mvc:interceptors>
四、多个拦截器的执行顺序
在 Spring MVC 中,多个拦截器可以组成一个拦截器链,按照注册(配置)顺序依次执行。
假设现在按顺序注册三个拦截器Interceptor1,Interceptor2,Interceptor3。
当所有的拦截器的preHandle方法都返回true时:
- preHandle执行顺序:Interceptor1->Interceptor2->Interceptor3 (顺序执行)
- postHandle执行顺序:Interceptor3->Interceptor2->Interceptor1 (逆序执行)
- afterCompletion执行顺序:Interceptor3->lpeAk;Interceptor2->Interceptor1 (逆序执行)
当某一个拦截器的preHandle方法返回false时,这里假设为Interceptor3
- preHandle执行顺序:Interceptor1->Interceptor2->Interceptor3 (顺序执行直到某一个拦截器返回false)
- postHandle不执行,控制器方法也不执行
- afterCompletion执行顺序:Interceptor2->Interceptor1 (返回false的拦截器之前的拦截器逆序执行)
为什么是这样的顺序呢?我们观察源码可以发现:
- preHandle源码分析
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { //interceptorList是一个ArrayList集合,按顺序存放了所有的拦截器 //下标从0开始,从这里我们可以知道为什么是顺序执行的。 //this.interceptorIndex = i++,注意这个代码,如果返回false,则它的值则表示当前返回false的拦截器的下标 for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex = i++) { HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i); //如果返回false if (!interceptor.preHandle(request, response, this.handler)) { //执行AfterCompletion,这里我们就知道为什么不执行postHandle,而执行AfterCompletion了 this.triggerAfterCompletion(request, response, (Exception)null); return false; } } return true; }
- postHandle源码分析
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception { //可以看到这里是从最后一个拦截器开始逆序遍历 for(int i = this.interceptorList.size() - 1; i >= 0; --i) { HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i); interceptor.postHandle(request, response, this.handler, mv); } }
- afterCompletion源码分析
void apythonpplyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception { //this.interceptorList.size() - 1表示当前返回false的拦截器的上一个的下标 //注意这里是--i //这也就解释了为什么是返回false的拦截器之前的拦截器逆序执行 for(int i = this.interceptorList.size() - 1; i >= 0; --i) { HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i); interceptor.postHandle(request, response, this.handler, mv); } }
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程客栈(www.devze.com)。
精彩评论