SpringMVC --- 拦截器
拦截器
在web开发中,拦截器是经常用到的功能。它可以帮我们验证是否登陆、预先设置数据以及统计方法的执行效率等等。
spring中拦截器主要分两种,一个是HandlerInterceptor,一个是MethodInterceptor。
HandlerInterceptor是SpringMVC项目中的拦截器,它拦截的目标是请求的地址,比MethodInterceptor先执行。
SpringMVC 中的Interceptor拦截请求是通过HandlerInterceptor 来实现的。在SpringMVC 中定义一个Interceptor 非常简单,主要有两种方式,第一种方式是要定义的Interceptor类要实现了Spring 的HandlerInterceptor 接口,或者是这个类继承实现了HandlerInterceptor 接口的类,比如Spring 已经提供的实现了HandlerInterceptor 接口的抽象类HandlerInterceptorAdapter ;第二种方式是实现Spring的WebRequestInterceptor接口,或者是继承实现了WebRequestInterceptor的类。
1、拦截器类的编写
- 第一种方法,实现 HandlerInterceptor 接口,重写其中的方法(注意:是方法重写,因为HandlerInterceptor接口中的所有方法),HandlerInterceptor接口中有三个默认方法(default 修饰)
- 第二种方法:继承 HandlerInterceptorAdapter类(已过时,和HandlerInterceptor一样)
- 实现Spring的 WebRequestInterceptor 接口,或者是继承实现了WebRequestInterceptor的类。
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
}
}
2、拦截器的配置
SpringMVC中的拦截器用于拦截控制器方法的执行
SpringMVC中的拦截器需要实现 HandlerInterceptor
SpringMVC的拦截器必须在SpringMVC的配置文件中进行配置:
<!--配置拦截器-->
<!--所有的请求都会被拦截-->
<mvc:interceptors>
<!--第一种方式-->
<!-- <bean class="com.atguigu.mvc.interceptors.FirstInterceptor"></bean>-->
<!--第二种方式-->
<!--<ref bean="firstInterceptor"></ref>-->
<!--第三种方式-->
<!--该方法能指定拦截规则,指定哪些拦截,哪些不拦截-->
<!--拦截除主页面的所有请求-->
<mvc:interceptor>
<!--ant风格 /** -->
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/"/>
<ref bean="firstInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
<!--
以上配置方式可以通过ref或bean标签设置拦截器,通过mvc:mapping设置需要拦截的请求,通过mvc:exclude-mapping设置需要排除的 请求,即不需要拦截的请求
-->
3、拦截器的三个抽象方法
SpringMVC中的拦截器有三个抽象方法:
preHandle:控制器方法执行之前执行preHandle(),其boolean类型的返回值表示是否拦截或放行,返回true为放行,即调用 控制器方法;返回false表示拦截,即不调用控制器方法;
该方法在进入Handler(Controller)方法执行之前执行此方法
应用场景:如身份认证,身份授权。
postHandle:控制器方法执行之后执行postHandle()
该方法在进入Handler(Controller)方法之后,返回ModelAndView之前执行
应用场景从modelAndView出发,将公用模型数据(如菜单导航)在这里传到视图,也可以在这里统一制定视图
afterComplation:处理完视图和模型数据,渲染视图完毕之后执行afterComplation()
该方法在handler方法执行完之后执行
应用场景:统一日志处理,统一异常处理
4、多个拦截器的执行顺序
a> 若每个拦截器的preHandle()都返回true
此时多个拦截器的执行顺序和拦截器在SpringMVC的配置文件的配置顺序有关:
preHandle()会按照配置的顺序执行,而postHandle()和afterComplation()会按照配置的反序执行
b> 若某个拦截器的preHandle()返回了false
preHandle()返回false和它之前的拦截器的preHandle()都会执行,postHandle()都不执行,返回false的拦截器之前的拦截器的afterComplation()会执行
源码解读
processDispatchResult 方法
applyPreHandle(processedRequest, response) 方法:正序执行所有的preHandle方法
applyPostHandle(processedRequest, response, mv) 方法,前提是所有 preHandle方法都返回true,才倒序执行所有的postHandle方法,否则一个不执行
triggerAfterCompletion(request, response, null) 方法:是在processDispatchResult 方法调用,是倒序执行 preHandle 返回 false 之前的拦截器(不包括返回 false的拦截器)的 afterCompletion方法
try {
try {
//所有 preHandle方法执行
//如果存在 preHandle 返回 false,则返回false,则执行 return;挑出该层 try-catch
//执行控制器方法
//所有 postHandle方法倒序执行,如果有一个 preHandle 返回 false 则一个都不会执行
} catch{
} catch{
}
//processDispatchResult 方法执行,渲染视图和倒序执行preHandle返回false之前的 afterCompletion 方法
}catch{
}catch{
}finally{}
在DispatcherServlet 源码中,关于执行控制器方法的源码部分(在源码中1061 行上下,结构是以上述代码块的结构)
当有preHandle() 返回 false 时,里层 进入 if语句 执行 return; 所以 postHandle() 方法一个不会执行,而 afterCompletion() 方法会执行一部分,原因是 执行 afterCompletion() 方法是在 外层的 try - catch 中,而 preHandle() 结束的是里层的 try-catch,afterCompletion() 还是会执行到。