Spring MVC拦截器
一、概述

二、简介
SpringMVC 的拦截器 Interceptor 规范,主要是对 Controller 资源访问时进行拦截操作的技术,当然拦截后可以进行权限控制,功能增强等都是可以的。拦截器有点类似 Javaweb 开发中的 Filter,拦截器与 Filter 的区别如下图:

2.1Filtery与Interceptor对比:

2.2拦截器Interceptor简介:
实现了 HandlerInterceptor 接口,且被 Spring 管理的 Bean 都是拦截器,接口定义如下:
java
package com.itheima.interceptors;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor1 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptor1...preHandle");
return true; //是否放行 true放行
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor1...postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyInterceptor1...afterCompletion");
}
}
实际上Controller层的方法也就是所谓的Handler处理器(目标方法),preHandle方法在目标方法之前执行,返回true目标方法才有机会执行, postHandle在目标方法之后执行,不一定执行,afterCompletion是在视图渲染完毕,在最后执行(有点类似于try-catch方法中的finally方法)。(全部执行完之后才执行),下面是三个方法的一些参数:

三、HandlerInterceptor拦截器快速入门
3.1先实现HandlerInterceptor接口定义自己的拦截器
java
package com.itheima.interceptors;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptorm1 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("HandlerInterceptor...preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("HandlerInterceptor...postHandle");
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("HandlerInterceptor...afterCompletion");
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
preHandle方法是最重要的,直接关乎到资源是否能够访问。
3.2在SpringMVC容器中配置拦截器
java
<!-- 配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!-- 这里实际上是配置拦截的路径-->
<!-- /*是只能拦截下一层目录的所有,/**是拦截多层目录的-->
<mvc:mapping path="/**"/>
<bean class="com.itheima.interceptors.MyInterceptor1"/>
</mvc:interceptor>
</mvc:interceptors>
3.3实验测试
1、正常访问,preHandle返回true(代表可以访问目标资源):

2、当让preHandle返回false时,只有preHandle,目标方法无法执行, postHandle、afterCompletion又是跟随preHandle:

四、拦截器执行顺序
4.1总述

4.2Interceptor本身执行顺序、preHandle全部返回true


只与配置顺序有关,这种情况又恰好是preHandle全部返回true的情况,所以直接借这个实验说明了:

4.3preHandle有返回false的

两个拦截器,第2个返回false:

五、拦截器执行原理
从前段控制器的源码来分析原理:
java
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
mappedHandler = this.getHandler(processedRequest);
HandlerExecutionChain:
java
public class HandlerExecutionChain {
private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);
private final Object handler;
private final List<HandlerInterceptor> interceptorList;
private int interceptorIndex;
主要是一些目标资源和拦截器,和上面的流程图对应了。
java
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
java
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex = i++) {
HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
if (!interceptor.preHandle(request, response, this.handler)) {
this.triggerAfterCompletion(request, response, (Exception)null);
return false;
}
}
return true;
}
执行前置方法由小到大遍历。
java
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
这就是执行目标方法。
java
mappedHandler.applyPostHandle(processedRequest, response, mv);
java
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);
}
}
执行后置方法,倒序地遍历。
java
this.processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
java
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) {
mappedHandler.triggerAfterCompletion(request, response, (Exception)null);
}
}
}
mappedHandler.triggerAfterCompletion(request, response, (Exception)null); ,是执行最终方法了:
java
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
for(int i = this.interceptorIndex; i >= 0; --i) {
HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
try {
interceptor.afterCompletion(request, response, this.handler, ex);
} catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
顺序是倒序了,这段原理分析同时也验证了拦截器的执行顺序。