SpringMVC源码剖析(六)- HandlerInterceptor的来龙去脉

来源:互联网 发布:美工助手阿里巴巴版 编辑:程序博客网 时间:2024/06/04 22:47

Spring MVC 中的Interceptor 拦截器的主要作用是拦截用户的请求并进行相应的处理。比如通过它来进行权限验证,或者是来判断用户是否登陆。类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。更准确的说,我更认为它更像Spring AOP里面的环绕通知,在我们调用处理器的前后进行处理。

其实整个spring mvc的大致请求过程如下所示:


那么HandlerInterceptor在Spring MVC中是如何起作用的呢?我们知道DispatcherServlet是处理请求的核心的servlet,也就是我们常说的前端控制器,通过我前面的几篇博客也知道,DispatcherServlet中的doDispatch方法是处理请求的核心方法,我们不妨进去看一下:

/** * Process the actual dispatching to the handler. * <p>The handler will be obtained by applying the servlet's HandlerMappings in order. * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters * to find the first that supports the handler class. * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers * themselves to decide which methods are acceptable. * @param request current HTTP request * @param response current HTTP response * @throws Exception in case of any kind of processing failure */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 || mappedHandler.getHandler() == 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 = "GET".equals(method);if (isGet || "HEAD".equals(method)) {long lastModified = ha.getLastModified(request, mappedHandler.getHandler());if (logger.isDebugEnabled()) {logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);}if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;}}//调用拦截器的前置处理if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}try {// 利用handlerAdaper去调用处理器,在handlerAdaper里面回调了handler的方法.mv = ha.handle(processedRequest, response, mappedHandler.getHandler());}finally {if (asyncManager.isConcurrentHandlingStarted()) {return;}}applyDefaultViewName(request, mv);//调用拦截器的后置处理mappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {dispatchException = ex;}processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {triggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Error err) {triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);}finally {if (asyncManager.isConcurrentHandlingStarted()) {// Instead of postHandle and afterCompletionmappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);return;}// Clean up any resources used by a multipart request.if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}}

上面就是DispatcherServlet类中的doDispatch方法的源码,其实我们如果单看拦截器的作用的话,总结下来就是下面的核心代码:

//调用拦截器的前置处理if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}try {// 利用handlerAdaper去调用处理器,在handlerAdaper里面回调了handler的方法.mv = ha.handle(processedRequest, response, mappedHandler.getHandler());}//调用拦截器的后置处理mappedHandler.applyPostHandle(processedRequest, response, mv);
mappedHandler的变量其实就是HandlerExecutionChain对象,其实在DispatcherServlet里面,框架的handler的具体对象和HandlerInterceptor的具体对象并没有直接对在这里也就是DispatcherServlet里面暴露,而是以HandlerExecutionChain对象暴露出去,你可以认为HandlerExecutionChain对象是handler和HandlerInterceptor的组合。分析到这里,我们研究的重点HandlerInterceptor终于露出水面了。

public interface HandlerInterceptor {boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)    throws Exception;void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)throws Exception;void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception;}
通过源码,可以很清楚的看到,其实HandlerInterceptor就3个方法,三个方法的解释如下:

   (1).preHandle (HttpServletRequest request, HttpServletResponse response, Object handle) 方法,顾名思义,该方法将在请求处理之前进行调用。SpringMVC 中的Interceptor 是链式的调用的,在一个应用中或者说是在一个请求中可以同时存在多个Interceptor 。每个Interceptor 的调用会依据它的声明顺序依次执行,而且最先执行的都是Interceptor 中的preHandle 方法,所以可以在这个方法中进行一些前置初始化操作或者是对当前请求的一个预处理,也可以在这个方法中进行一些判断来决定请求是否要继续进行下去。该方法的返回值是布尔值Boolean 类型的,当它返回为false 时,表示请求结束,后续Interceptor 和Controller 都不会再执行;当返回值为true 时就会继续调用下一个Interceptor 的preHandle 方法,如果已经是最后一个Interceptor 的时候就会是调用当前请求的Controller 方法。
   (2).postHandle (HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView) 方法,由preHandle 方法的解释我们知道这个方法只能是在当前所属的Interceptor 的preHandle 方法的返回值为全部都为true 时才能被调用。postHandle 方法,顾名思义就是在当前请求进行处理之后,也就是Controller 方法调用之后执行,但是它会在DispatcherServlet 进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller 处理之后的ModelAndView 对象进行操作。postHandle 方法被调用的方向跟preHandle 是相反的,也就是说先声明的Interceptor 的postHandle 方法反而会后执行,这和Struts2 里面的Interceptor 的执行过程有点类型。Struts2 里面的Interceptor 的执行过程也是链式的,只是在Struts2 里面需要手动调用ActionInvocation 的invoke 方法来触发对下一个Interceptor 或者是Action 的调用,然后每一个Interceptor 中在invoke 方法调用之前的内容都是按照声明顺序执行的,而invoke 方法之后的内容就是反向的。
   (3).afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) 方法,该方法的执行是很诡异的。该方法会在当前对应的Interceptor 的preHandle 方法的返回值至少有一个为true 时才会执行。是不是不太好理解,例如有以下几种情况(比如我们有三个拦截器):第一,如果三个拦截器的preHandle的返回值都是true,那么这个时候框架的afterCompletion是会被执行的,而且是倒叙执行。第二,如果第一个拦截器的preHandle的返回值是false,那么本次请求就到此结束了,后面的都不会被执行。第三,如果第一个拦截器的preHandle的返回值都是true,到第二个拦截器的preHandle的返回值都是false,那么第一个拦截器的afterCompletion是会被执行的。第四,如果第一个和第二个拦截器的preHandle的返回值都是true,而第三个拦截器的preHandle的返回值是false,那么第一个和第二个拦截器的afterCompletion是会被执行的,并且是倒叙执行。第五,只要有一个拦截器的preHandle的返回值是false,postHandle方法都不会被执行。


使用mvc:interceptors标签来声明需要加入到SpringMVC拦截器链中的拦截器。
然后它们就可以形成一个拦截器链,拦截器的执行顺序是按声明的先后顺序执行的。
在mvc:interceptors标签下声明interceptor主要有两种方式:
(1)直接定义一个Interceptor实现类的bean对象。使用这种方式声明的Interceptor拦截器将会对所有的请求进行拦截。
(2)使用mvc:interceptor标签进行声明。使用这种方式进行声明的Interceptor可以通过mvc:mapping子标签来定义需要进行拦            截的请求路径。
经过上述两步之后,定义的拦截器就会发生作用对特定的请求进行拦截了。

0 0
原创粉丝点击