文章目录
1.认识拦截器
在SpringMVC中,存在与Servlet中的过滤器(Filter)类似并且功能功能更加强大的拦截器(Interceptor)。它主要用于拦截用户请求并作相应的操作。可以简单理解为Spring中AOP的思想实现。在SpringMVC中,拦截器(HandlerInterceptor)是一个接口,我们使用时可以通过重写接口方法来完成我们想要做的事情。
public interface HandlerInterceptor {
/*
在控制器方法执行前运行该方法,返回值表示是否中断后续操作
返回值: true:继续向下执行
false:会中断后续的所有操作(包括调用下一个拦截器和控制器类中的方法执行等)。
*/
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.配置拦截器
2.1 编写自己的拦截器
public class MyFirstHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyFirstHandlerInterceptor----preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyFirstHandlerInterceptor----postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyFirstHandlerInterceptor----afterCompletion");
}
}
2.2 编写请求处理方法
@RequestMapping("/test11")
@ResponseBody
public String test11() {
System.out.println("方法执行");
return "success";
}
2.3 在SpringMVC.xml中注册我们的拦截器
<mvc:interceptors>
<!-- 方法一:给某些请求配置拦截器-->
<mvc:interceptor>
<mvc:mapping path="/test0*"/>
<mvc:exclude-mapping path="/test1*"/>
<bean class="com.shang.HandlerInterceptor.MyFirstHandlerInterceptor"></bean>
</mvc:interceptor>
<!-- 方法二:全部都进行配置-->
<bean class="com.shang.HandlerInterceptor.MyFirstHandlerInterceptor"></bean>
</mvc:interceptors>
- < mvc:interceptors > 元素用于配置一组拦截器,基子元素的bean中定义的是全局拦截器,他会拦截所有的请求
- < mvc:interceptor >元素定义的是指定路径的拦截器
- < mvc:mapping path= >指定拦截器作用的路径。”/**”表示拦截所有路径,“/test1 *” 表示拦截所有以 “/test1 *” 结尾的路径
- < mvc:exclude-mapping path= > 指定拦截不包括那些路径
2.4 结果测试
3.多个拦截器执行顺序
定义两个拦截器,在SpringMVC.xml中如下配置
MyFirstHandlerInterceptor
public class MyFirstHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyFirstHandlerInterceptor----preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyFirstHandlerInterceptor----postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyFirstHandlerInterceptor----afterCompletion");
}
}
MySecondHandlerInterceptor.java
public class MySecondHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MySecondHandlerInterceptor----preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MySecondHandlerInterceptor----postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MySecondHandlerInterceptor----afterCompletion");
}
}
SpringMVC.xml
<mvc:interceptors>
<!-- 方法一:给某些请求配置拦截器-->
<mvc:interceptor>
<mvc:mapping path="/test*"/>
<bean class="com.shang.HandlerInterceptor.MyFirstHandlerInterceptor"></bean>
</mvc:interceptor>
<!-- 方法二:全部都进行配置-->
<bean class="com.shang.HandlerInterceptor.MySecondHandlerInterceptor"></bean>
</mvc:interceptors>
3.1 每个拦截器全部放行
运行结果如下
我们通过输出可以看出,在SpringMVC.xml中进行配置的顺序就是我们拦截器执行的顺序,在退出控制器方法执行后,拦截器安装之前的顺序逆序退出,用符号来表示一下,就是这样的:
{ [ * ] }
3.2 第二个拦截器不放行
将MySecondHandlerInterceptor中的preHandle返回改为false
两个拦截器的perHandle执行了,但是我们可以看见MyFirstHandlerInterceptor的afterCompletion方法也执行了.且方法不执行,那这是为什么呢?
4.拦截器源码解析
在所有的请求发送过来时,都会执行DispatcherServlet.doDispatch方法,我们从这个方法开始了解拦截器的执行过程.
4.1 doDispatch方法
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request. 获得请求的处理器链
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request. 获得请求处理器的适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = HttpMethod.GET.matches(method);
if (isGet || HttpMethod.HEAD.matches(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//这里执行拦截器的preHandle方法,且这里传入的是一个拦截器链,如果返回false,不执行下面的方法,直接跳到finally处
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler. 真正执行执行器方法返回ModelAndView对象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
//这里执行拦截器链的postHandle的方法,逆序执行
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
//视图结果返回到浏览器的执行
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
//处理异常,在preHandle的控制中,返回false,就会执行.
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
4.2 applyPreHandle方法
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
//对拦截器链进行遍历执行
for (int i = 0; i < this.interceptorList.size(); i++) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
//如果preHandle返回值为false,退出方法执行
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
//记录执行到第几个拦截器链
this.interceptorIndex = i;
}
return true;
}
4.3 applyPostHandle方法
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
throws Exception {
//从循环可以看出,为什么拦截器执行是逆序的
for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
interceptor.postHandle(request, response, this.handler, mv);
}
}
4.4 triggerAfterCompletion方法
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
//这里就解释了为什么第二个拦截器挂了,第一个拦截器的afterCompletion方法还可以运行.
//4.2-4.4的这些方法都存在于一个类中,interceptorIndex记录了最后执行到的拦截器
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}