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

  • 相关阅读:
    OpenGL模板缓冲区与模板测试
    u3d调用c++ dll的DllNotFoundExceion 问题
    u3d调用自己的dll
    使用 idea 的Bookmarks(书签)功能
    maven 相关问题
    项目
    使用 vi/vim 时,粘贴进新创建文件或空文件的首行内容丢失的解决方法
    Thread.sleep还是TimeUnit.SECONDS.sleep
    搭建 Guacamole 并解决各种坑和创建不了虚拟驱动器导致无法实现文件传输的方法
    properJavaRDP 跑通本地远程桌面
  • 原文地址:https://www.cnblogs.com/abcwt112/p/6048012.html
Copyright © 2020-2023  润新知