SpringMVC学习记录5

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去调用.

 

posted @ 2016-11-09 18:23  abcwt112  阅读(227)  评论(0编辑  收藏  举报