Springmvc流程中的扩展点有很多,可以在很多地方插入自己的代码逻辑达到控制流程的目的.
如果要对Controller的handler方法做统一的处理.我想应该会有很多选择,比如:@ModelAttribute @InitBinder @ExceptionHandler @ControllerAdvice等注解,自己写AOP包裹Controller,Interceptor等等..
我从来没有用过Interceptor,所以最近稍微花了点时间研究了下.
流程
先看看在Springmvc中Interceptor拦截器是在什么时候被调用的.
1 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { 2 HttpServletRequest processedRequest = request; 3 HandlerExecutionChain mappedHandler = null; 4 boolean multipartRequestParsed = false; 5 6 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); 7 8 try { 9 ModelAndView mv = null; 10 Exception dispatchException = null; 11 12 try { 13 processedRequest = checkMultipart(request); 14 multipartRequestParsed = (processedRequest != request); 15 16 // Determine handler for the current request. 17 mappedHandler = getHandler(processedRequest); 18 if (mappedHandler == null || mappedHandler.getHandler() == null) { 19 noHandlerFound(processedRequest, response); 20 return; 21 } 22 23 // Determine handler adapter for the current request. 24 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); 25 26 // Process last-modified header, if supported by the handler. 27 String method = request.getMethod(); 28 boolean isGet = "GET".equals(method); 29 if (isGet || "HEAD".equals(method)) { 30 long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); 31 if (logger.isDebugEnabled()) { 32 logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified); 33 } 34 if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { 35 return; 36 } 37 } 38 39 if (!mappedHandler.applyPreHandle(processedRequest, response)) { 40 return; 41 } 42 43 // Actually invoke the handler. 44 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 45 46 if (asyncManager.isConcurrentHandlingStarted()) { 47 return; 48 } 49 50 applyDefaultViewName(request, mv); 51 mappedHandler.applyPostHandle(processedRequest, response, mv); 52 } 53 catch (Exception ex) { 54 dispatchException = ex; 55 } 56 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); 57 } 58 catch (Exception ex) { 59 triggerAfterCompletion(processedRequest, response, mappedHandler, ex); 60 } 61 catch (Error err) { 62 triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err); 63 } 64 finally { 65 if (asyncManager.isConcurrentHandlingStarted()) { 66 // Instead of postHandle and afterCompletion 67 if (mappedHandler != null) { 68 mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); 69 } 70 } 71 else { 72 // Clean up any resources used by a multipart request. 73 if (multipartRequestParsed) { 74 cleanupMultipart(processedRequest); 75 } 76 } 77 } 78 }
从dispatcherServlet中可以看出大致的工作流程:
第44行是我们比较熟悉的,让HandlerAdapter来调用我们自己写的Controller的handler来处理request.
在此之前的39行是调用包装了handler的HandlerExecutionChain的前置处理方法,第51行是调用了HandlerExecutionChain的后置处理方法.
而HandlerExecutionChain的applyPreHandle与applyPostHandle里面会调用拦截器的相关方法.
HandlerExecutionChain
handler方法会和各种Interceptor包装成HandlerExecutionChain给dispatcherServlet调用,所以有必要看看HandlerExecutionChain里的方法.
1 /** 2 * Apply preHandle methods of registered interceptors. 3 * @return {@code true} if the execution chain should proceed with the 4 * next interceptor or the handler itself. Else, DispatcherServlet assumes 5 * that this interceptor has already dealt with the response itself. 6 */ 7 boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { 8 HandlerInterceptor[] interceptors = getInterceptors(); 9 if (!ObjectUtils.isEmpty(interceptors)) { 10 for (int i = 0; i < interceptors.length; i++) { 11 HandlerInterceptor interceptor = interceptors[i]; 12 if (!interceptor.preHandle(request, response, this.handler)) { 13 triggerAfterCompletion(request, response, null); 14 return false; 15 } 16 this.interceptorIndex = i; 17 } 18 } 19 return true; 20 } 21 22 /** 23 * Apply postHandle methods of registered interceptors. 24 */ 25 void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception { 26 HandlerInterceptor[] interceptors = getInterceptors(); 27 if (!ObjectUtils.isEmpty(interceptors)) { 28 for (int i = interceptors.length - 1; i >= 0; i--) { 29 HandlerInterceptor interceptor = interceptors[i]; 30 interceptor.postHandle(request, response, this.handler, mv); 31 } 32 } 33 } 34 35 /** 36 * Trigger afterCompletion callbacks on the mapped HandlerInterceptors. 37 * Will just invoke afterCompletion for all interceptors whose preHandle invocation 38 * has successfully completed and returned true. 39 */ 40 void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex) 41 throws Exception { 42 43 HandlerInterceptor[] interceptors = getInterceptors(); 44 if (!ObjectUtils.isEmpty(interceptors)) { 45 for (int i = this.interceptorIndex; i >= 0; i--) { 46 HandlerInterceptor interceptor = interceptors[i]; 47 try { 48 interceptor.afterCompletion(request, response, this.handler, ex); 49 } 50 catch (Throwable ex2) { 51 logger.error("HandlerInterceptor.afterCompletion threw exception", ex2); 52 } 53 } 54 } 55 }
这3个方法刚好对应了HandlerInterceptor里的3个方法:
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;
从Interceptor方法名字中我们就可以看出来这3个方法的作用:handler方法之前调用,handler方法之后调用,dispatch之后调用(视图渲染完之后).
如果配置了多个拦截器,从HandlerExecutionChain中的3个方法里也可以看出:
1.如果前置处理的顺序是拦截器1,拦截器2,拦截器3.那后置处理的顺序是拦截器3,拦截器2,拦截器1
2.如果一个拦截器前置处理的时候返回了false.那么后面的拦截器就不需要执行了.比如拦截2前置方法返回false.那拦截器3就不会执行.
3.在2前置处理失败的情况下,..调用过前置处理方法的拦截器的afterCompletion方法会被调用,用来清理资源...从代码中可以看出afterCompletion和后置处理方法是一样,也是拦截器顺序的倒序执行的,不过只有前置方法返回true的拦截器才会触发(因为拦截器3都没触发,所以不需要触发afterCompletion来清理资源).
多个拦截器的顺序
从HandlerExecutionChain中已经可以看出配置多个拦截器时候的调用顺序了...那怎么确定配置的多个拦截器在HandlerExecutionChain中的加载顺序呢?
实践结果
我实践的结果是在XML里配置的顺序就是加载顺序..
1 <mvc:interceptors> 2 <mvc:interceptor> 3 <mvc:mapping path="/**" /> 4 <bean class="com.labofjet.controller.TestInteceptor1"></bean> 5 </mvc:interceptor> 6 7 <mvc:interceptor> 8 <mvc:mapping path="/**" /> 9 <bean class="com.labofjet.controller.TestInteceptor2"></bean> 10 </mvc:interceptor> 11 </mvc:interceptors>
比如这样就是先进入拦截1,再进入拦截2.
分析思考过程
记录一下我的思考过程,便于以后复习:
从DispatcherServlet的doDispatcher放中会调用这个方法:
1 protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { 2 for (HandlerMapping hm : this.handlerMappings) { 3 if (logger.isTraceEnabled()) { 4 logger.trace( 5 "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'"); 6 } 7 HandlerExecutionChain handler = hm.getHandler(request); 8 if (handler != null) { 9 return handler; 10 } 11 } 12 return null; 13 }
说明HandlerExecutionChain 来自HandlerMapping,在一般的情况下应该是RequestMappingHandlerMapping.因为RequestMappingHandlerMapping的order是最高的.所以它返回了handler以后就直接作为方法的结果返回了.
1 @Override 2 public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { 3 Object handler = getHandlerInternal(request); 4 if (handler == null) { 5 handler = getDefaultHandler(); 6 } 7 if (handler == null) { 8 return null; 9 } 10 // Bean name or resolved handler? 11 if (handler instanceof String) { 12 String handlerName = (String) handler; 13 handler = getApplicationContext().getBean(handlerName); 14 } 15 return getHandlerExecutionChain(handler, request); 16 }
L15说明HandlerExecutionChain来自getHandlerExecutionChain方法.
1 protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { 2 HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? 3 (HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); 4 chain.addInterceptors(getAdaptedInterceptors()); 5 6 String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); 7 for (MappedInterceptor mappedInterceptor : this.mappedInterceptors) { 8 if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) { 9 chain.addInterceptor(mappedInterceptor.getInterceptor()); 10 } 11 } 12 13 return chain; 14 }
interceptor来么来自adaptedInterceptors要么来自mappedInterceptors.
打断点发现是来自mappedInterceptors,而adaptedInterceptors是给我们自己扩展去加载自定义拦截器用的.
再思考下:
RequestMappingHandlerMapping是初始化Springmvc的时候加载的bean,Controller的handler方法是Springmvc初始化的时候就找出来并包装好的.所以Interceptor没有理由不在Springmvc初始化的时候找出来并包装好.毕竟这handler和interceptor功能都差不多....
我粗略看了下RequestMappingHandlerMapping在初始化的时候似乎只找到了2个地方可以参与Spring声明周期:
第一个是:
1 /** 2 * Detects handler methods at initialization. 3 */ 4 @Override 5 public void afterPropertiesSet() { 6 initHandlerMethods(); 7 } 8 9 /** 10 * Scan beans in the ApplicationContext, detect and register handler methods. 11 * @see #isHandler(Class) 12 * @see #getMappingForMethod(Method, Class) 13 * @see #handlerMethodsInitialized(Map) 14 */ 15 protected void initHandlerMethods() { 16 if (logger.isDebugEnabled()) { 17 logger.debug("Looking for request mappings in application context: " + getApplicationContext()); 18 } 19 20 String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ? 21 BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) : 22 getApplicationContext().getBeanNamesForType(Object.class)); 23 24 for (String beanName : beanNames) { 25 if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX) && 26 isHandler(getApplicationContext().getType(beanName))){ 27 detectHandlerMethods(beanName); 28 } 29 } 30 handlerMethodsInitialized(getHandlerMethods()); 31 }
afterPropertiesSet这里是为了找到所有handler方法.
第二个是:
1 /** 2 * Initializes the interceptors. 3 * @see #extendInterceptors(java.util.List) 4 * @see #initInterceptors() 5 */ 6 @Override 7 protected void initApplicationContext() throws BeansException { 8 extendInterceptors(this.interceptors); 9 detectMappedInterceptors(this.mappedInterceptors); 10 initInterceptors(); 11 }
这里initApplicationContext就是初始化interceptor用的.
前面实践打断点发现是mappedInterceptors,所以是调用detectMappedInterceptors来加载我定义的拦截器的.
/** * Detect beans of type {@link MappedInterceptor} and add them to the list of mapped interceptors. * <p>This is called in addition to any {@link MappedInterceptor}s that may have been provided * via {@link #setInterceptors}, by default adding all beans of type {@link MappedInterceptor} * from the current context and its ancestors. Subclasses can override and refine this policy. * @param mappedInterceptors an empty list to add {@link MappedInterceptor} instances to */ protected void detectMappedInterceptors(List<MappedInterceptor> mappedInterceptors) { mappedInterceptors.addAll( BeanFactoryUtils.beansOfTypeIncludingAncestors( getApplicationContext(), MappedInterceptor.class, true, false).values()); }
这个方法其实就是把所有MappedInterceptor类型的类找出来而已...我们定义的类型不是MappedInterceptor的子类..想必有什么地方把我们定义的Interceptor放到了MappedInterceptor类中.那是哪里呢? 我没有找到.......可能需要找到XML中字符串<mvc:interceptors>是怎么解析的...
不过这里已经得到了足够的信息,至少可以知道Interceptor读出来以后并没有进行什么排序,所以读出来的顺序应该就是List<MappedInterceptor> mappedInterceptors的顺序.
总结
简单总结一下
拦截器的流程:
请求 -> DispatcherServlet -> 各个拦截器定义顺序来执行前置拦截方法 -> 各个拦截器定义顺序倒序来执行后置拦截方法 -> 处理视图 -> 拦截器定义倒序顺序回收资源
RequestMappingHandlerMapping:
找到所有MappedInterceptor拦截器并设置到属性里.
找到handler方法并设置到属性里.
当有请求到DispatcherServlet的时候把2者结合成HandlerExecutionChain丢给DispatcherServlet去调用.