一、介绍
(1)SpringMVC的拦截器用于拦截控制器方法的执行。
(2)要使用拦截器需要实现HandlerInterceptor接口。
(3)拦截器需要在SpringMVC的配置文件中配置。
(4)拦截器中的三个方法preHandler()、postHandle()、afterCompletion(),分别在控制器方法执行前、控制器方法执行后、渲染后执行。
二、源码分析
(1)拦截器中三个方法的执行顺序为preHandler()、postHandle()、afterCompletion()
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
try {
ModelAndView mv = null;
Object dispatchException = null;
try {
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
mappedHandler = this.getHandler(processedRequest);
if (mappedHandler == null) {
this.noHandlerFound(processedRequest, response);
return;
}
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
return;
}
}
//在执行handle()方法前,也就是执行控制器方法前,
//先执行配置好的拦截器中的preHandle()方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//执行控制器方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
this.applyDefaultViewName(processedRequest, mv);
//控制器方法执行后,执行配置好的拦截器中的postHandle()方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception var20) {
dispatchException = var20;
} catch (Throwable var21) {
dispatchException = new NestedServletException("Handler dispatch failed", var21);
}
//该方法内部会执行视图渲染,渲染完毕则执行配置好的拦截器中的afterCompletion()方法,具体分析见下文
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
} catch (Exception var22) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
} catch (Throwable var23) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
}
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else if (multipartRequestParsed) {
this.cleanupMultipart(processedRequest);
}
}
}
(2)processDispatchResult()方法
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
this.logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException)exception).getModelAndView();
} else {
Object handler = mappedHandler != null ? mappedHandler.getHandler() : null;
mv = this.processHandlerException(request, response, handler, exception);
errorView = mv != null;
}
}
if (mv != null && !mv.wasCleared()) {
//进行视图渲染
this.render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
} else if (this.logger.isTraceEnabled()) {
this.logger.trace("No view rendering, null ModelAndView returned.");
}
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
//执行配置好的拦截器中的afterCompletion()方法
mappedHandler.triggerAfterCompletion(request, response, (Exception)null);
}
}
}
(3)applyPreHandle()方法
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = this.getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
//按拦截器在配置文件中的配置顺序遍历
for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) {
HandlerInterceptor interceptor = interceptors[i];
//如果拦截器中的preHandle()方法返回false,
//则直接执行triggerAfterCompletion()方法,然后跳出DispatcherServlet,
//注意,不一定执行triggerAfterCompletion()中的所有配置好的拦截器的afterCompletion()方法,
//跟循环中的interceptorIndex有关
if (!interceptor.preHandle(request, response, this.handler)) {
this.triggerAfterCompletion(request, response, (Exception)null);
return false;
}
}
}
return true;
}
(4)applyPostHandle()方法
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
HandlerInterceptor[] interceptors = this.getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
//该方法只会在所有配置好的拦截器中的preHandle()方法均返回true后执行,
//会倒序执行所有配置好的拦截器中的postHandle()方法
for(int i = interceptors.length - 1; i >= 0; --i) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
}
(5)triggerAfterCompletion()方法
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) throws Exception {
HandlerInterceptor[] interceptors = this.getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
//一直执行到最后一个preHandle()方法返回true的拦截器的afterCompletion()方法
for(int i = this.interceptorIndex; i >= 0; --i) {
HandlerInterceptor interceptor = interceptors[i];
try {
interceptor.afterCompletion(request, response, this.handler, ex);
} catch (Throwable var8) {
logger.error("HandlerInterceptor.afterCompletion threw exception", var8);
}
}
}
}
三、项目测试
(1)编写拦截器FirstInterceptor、SecondInterceptor
public class FirstInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("FirstInterceptor...preHandle");
return true;
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("FirstInterceptor...postHandle");
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("FirstInterceptor...afterCompletion");
}
}
public class SecondInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("SecondInterceptor...preHandle");
return true;
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("SecondInterceptor...postHandle");
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("SecondInterceptor...afterCompletion");
}
}
(2)编写SpringMVC-config.xml,配置拦截器
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.wsh.controller"></context:component-scan>
<!-- 配置Thymeleaf视图解析器-->
<bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<!--视图解析器优先级-->
<property name="order" value="1"/>
<!--解析视图用的编码-->
<property name="characterEncoding" value="UTF-8"/>
<!--视图解析器模板引擎-->
<property name="templateEngine">
<bean class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<!--视图前缀-->
<property name="prefix" value="/WEB-INF/templates/"/>
<!--视图后缀-->
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML5"/>
<property name="characterEncoding" value="UTF-8"/>
</bean>
</property>
</bean>
</property>
</bean>
<mvc:interceptors>
<bean class="com.wsh.interceptor.FirstInterceptor"></bean>
<bean class="com.wsh.interceptor.SecondInterceptor"></bean>
</mvc:interceptors>
</beans>
(3)创建Controller类
@Controller
public class MyController {
@RequestMapping(value = {"/**"})
@ResponseBody
public String hello(){
return "hello";
}
}
(4)运行
四、配置选项
(1)在<mvc:interceptors>
标签中,如果拦截器用bean或ref标签配置,则对所有请求有效。
<mvc:interceptors>
<bean class="com.wsh.interceptor.FirstInterceptor"></bean>
<bean class="com.wsh.interceptor.SecondInterceptor"></bean>
</mvc:interceptors>
(2)在<mvc:interceptors>
标签中,如果拦截器用<mvc:interceptor>
标签配置,则可以自定义对哪些请求有效。
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.wsh.interceptor.FirstInterceptor"></bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/abc"/>
<bean class="com.wsh.interceptor.SecondInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>