SpringMVC拦截器HandlerInterceptor原理及使用

在使用SpringMVC拦截器的时候,我们接触的最多的便是HandlerInterceptor接口,因为我们所有的自定义拦截器都必须要实现HandlerInterceptor接口,那么就先从HandlerInterceptor接口开始一步步分析。

一、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 {
}

根据注释部分我们知道如下内容:

  1. preHandle是在找到处理handler对象的HandlerMapping之后,HandlerAdapter调度handler之前执行。

  2. postHandle是在HandlerAdapter调度handler之后,DispatcherServlet渲染视图之前执行,可以通过ModelAndView来向视图中添加一些信息等。

  3. afterCompletion是在渲染视图结束后执行,主要可以用来进行事后的资源清理。

  4. 其中postHandle和afterCompletion方法是反顺序执行的。也就是说第一个拦截器会最后一个执行。关于HandlerInterceptor的执行顺序我们可以在HandlerExecutionChain类中找到。

二、HandlerExecutionChain类

这个类由一个handler和若干的HandlerInterceptor构成。那么这个类的作用就显而易见了,就是将拦截器和handle组合起来执行。就是对handle进行了包装。

这个类中有几个主要的方法:

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
   HandlerInterceptor[] interceptors = getInterceptors();
   if (!ObjectUtils.isEmpty(interceptors)) {
      //按顺序执行
      for (int i = 0; i < interceptors.length; i++) {
         HandlerInterceptor interceptor = interceptors[i];
         if (!interceptor.preHandle(request, response, this.handler)) {
            triggerAfterCompletion(request, response, null);
            return false;
         }
         this.interceptorIndex = i;
      }
   }
   return true;
}
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
      throws Exception {

   HandlerInterceptor[] interceptors = getInterceptors();
   if (!ObjectUtils.isEmpty(interceptors)) {
      //逆序执行
      for (int i = interceptors.length - 1; i >= 0; i--) {
         HandlerInterceptor interceptor = interceptors[i];
         interceptor.postHandle(request, response, this.handler, mv);
      }
   }
}
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
      throws Exception {

   HandlerInterceptor[] interceptors = getInterceptors();
   if (!ObjectUtils.isEmpty(interceptors)) {
      // 逆序执行 
      for (int i = this.interceptorIndex; i >= 0; i--) {
         HandlerInterceptor interceptor = interceptors[i];
         try {
            interceptor.afterCompletion(request, response, this.handler, ex);
         }
         catch (Throwable ex2) {
            logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
         }
      }
   }
}

从函数名中我们可以看出这些方法分别是做什么的,分别是执行interceptorList中所有interceptor的preHandle、postHandle和afterCompletion方法

先从applyPreHandle()看起,我们发现这个方法就是做的这样一个工作,按照列表中interceptor的顺序来执行它们的preHandle方法,直到有一个返回false。再看一下返回false后这个方法所做的工作,这时会调用triggerAfterCompletion方法,此时this.interceptorIndex指向上一个返回true的interceptor的位置,所以它会按逆序执行所有返回true的interceptor的afterCompletion方法。换言之,也就是对于任意的返回false的interceptor都不会执行afterCompletion方法。而且是中断之前所有的preHandle执行完成之后才会执行afterCompletion方法。接下来是applyPostHandle(),这个方法较为简单,就是按照逆序执行所有interceptor的postHandle方法。最后的triggerAfterCompletion()也是一样,就是从最后一次preHandle成功的interceptor处逆序执行afterCompletion。

三、HandlerMapping接口

HandlerExecutionChain是通过HandlerMapping的getHandler方法返回的。继承该接口的类是来实现请求和handler对象的映射关系的。

这个接口中只有这样一个方法

HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

根据函数名,参数及返回值我们不难猜出这个接口的作用,就是根据request返回HandlerExecutionChain。至于HandlerMapping在springMVC中有多种实现,我们此处就不深究了。

对于getHandler最后的调度部分便是springMVC的最外层DispatcherServlet类了

四、DispatcherServlet类

DispatcherServlet类中调用HandlerMapping的getHandler的方法为getHandler(同名)

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
   if (this.handlerMappings != null) {
      for (HandlerMapping hm : this.handlerMappings) {
         if (logger.isTraceEnabled()) {
            logger.trace(
                  "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
         }
         HandlerExecutionChain handler = hm.getHandler(request);
         if (handler != null) {
            return handler;
         }
      }
   }
   return null;
}

从代码中不难看出整个逻辑就是依次判断servlet中的每个handlerMapping是否能够匹配该请求,直到找到那个匹配的然后返回处理结果。

对于HandlerExecutionChain的调用我们可以在doDispatch()中找到

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// 此处用processedRequest  需要注意的是:若是处理上传,processedRequest 将和request不再指向同一对象
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;

		//主要用来管理异步请求的处理。什么时候要用到异步处理呢?就是业务逻辑复杂(或者其他原因),为了避免请求线程阻塞,需要委托给另一个线程的时候。
		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		try {
			ModelAndView mv = null;
			Exception dispatchException = null;
			try {
				//检查是否为上传文件请求
				//checkMultipart 这个方法很重要,判断是否是上传需求。且看下面的具体分析:
				//如果请求是POST请求,并且请求头中的Context-Type是以multipart/开头的就认为是文件上传的请求
				processedRequest = checkMultipart(request);
				//标记一下:是否是文件上传的request
				multipartRequestParsed = (processedRequest != request);
				// 步骤1,获取执行链,重要
				// 找到一个处理器,如果没有找到对应的处理类的话,这里通常会返回404,如果throwExceptionIfNoHandlerFound属性值为true的情况下会抛出异常
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}
				// 步骤2,获取适配器,重要
				// 根据实际的handler去找到一个合适的HandlerAdapter,方法详细逻辑同getHandler,因此不再解释
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				//如果是GET请求,如果内容没有变化的话,则直接返回
				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;
					}
				}
				// 步骤3,执行拦截器pre方法,重要
				// 这段代码很有意思:执行处理器连里的拦截器们,具体参阅下面详细:
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}
				//步骤4,真正处理逻辑,重要
				// 真正执行我们自己书写的controller方法的逻辑。返回一个ModelAndView
				// 这也是一个很复杂的过程(序列化、数据绑定等等),需要后面专题讲解
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				// 如果异步启动了,这里就先直接返回了,也就不会再执行拦截器PostHandle之类的
				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}

				//意思是:如果我们没有设置viewName,就采用默认的。否则采用我们自己的
				applyDefaultViewName(processedRequest, mv);
				//步骤5,执行拦截器post方法,重要
				// 执行所有的拦截器的postHandle方法,并且把mv给他
				// 这里有一个小细节:这个时候拦截器是【倒序】执行的
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			catch (Throwable err) {
				dispatchException = new NestedServletException("Handler dispatch failed", err);
			}
			//步骤6,处理视图,重要
			//这个方法很重要,顾名思义,他是来处理结果的,渲染视图、处理异常等等的  下面详细分解
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
			//步骤7,执行拦截器收尾方法,重要
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Throwable err) {
			triggerAfterCompletion(processedRequest, response, mappedHandler,
					new NestedServletException("Handler processing failed", err));
		}
		finally {
			if (asyncManager.isConcurrentHandlingStarted()) {
				if (mappedHandler != null) {
			mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			}
			else {
				if (multipartRequestParsed) {
					cleanupMultipart(processedRequest);
				}
			}
		}
	}

从上面代码中我们可以验证第一部分所说的HandlerInterceptor接口中三个方法的执行顺序:

  1. preHandle是在找到处理handler对象的HandlerMapping之后,HandlerAdapter调度handler之前执行。
  2. postHandle是在HandlerAdapter调度handler之后,DispatcherServlet渲染视图之前执行
  3. afterCompletion是在渲染视图结束后执行

五、例子

  1. 四个拦截器A\B\C\D
@Component
public class A implements HandlerInterceptor {
    //在Controller执行之前调用,如果返回false,controller不执行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("---------A.preHandle--------");
        return true;
    }
    //controller执行之后,且页面渲染之前调用
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("---------A.postHandle--------");
    }
    //页面渲染之后调用,一般用于资源清理操作
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("---------A.afterCompletion--------");
    }
}
@Configuration
public class MvcConfiguration implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new A());
        registry.addInterceptor(new B());
        registry.addInterceptor(new C());
        registry.addInterceptor(new D());
        WebMvcConfigurer.super.addInterceptors(registry);
    }
}

当全部拦截器的preHandle返回值都是true时,调用拦截器返回如下:
SpringMVC拦截器HandlerInterceptor原理及使用_第1张图片

SpringMVC拦截器HandlerInterceptor原理及使用_第2张图片

当C的preHandle返回false时,返回如下:
SpringMVC拦截器HandlerInterceptor原理及使用_第3张图片

你可能感兴趣的:(Spring,SpringBoot,容器,spring,java)