• SpringMVC之处理流程


     之前在学servlet时写过JavaWeb与Asp.net工作原理比较分析,那篇主要是大致描述了下servlet的工作流程,今天在家了解了下springmvc的工作原理,与asp.net中的mvc进行了一下比较asp.net MVC 的处理流程,思想都是差不多,都是通过一个url怎么映射到类中做完处理返回浏览器的过程,首先要解决三大问题,一是url映射转换成request和response对象的问题二是浏览器与服务端的数据交互问题三是服务端的request、response怎么响应给客户端。今天了解了下它的运行流畅,其实网上也有好多教程。

    一图顶千言万语,用数据、用图说话,下图是springmvc的工作原理图。

    SpringMVC工作流程

    一、  用户发送请求至前端控制器DispatcherServlet。 

         1.DispatcherServlet它也是servlet,load-on-startup=1,tomcat启动时它也会初始化,初始化参数是contextConfigLocation上下文配置文件位置,参数值就是JavaWeb之Eclipse中使用Maven构建SpringMVC项目  配置的spring-mvc。在spring-mvc中可以配置自动扫描包名、默认注解映射支持、视图解释类、拦截器、对静态资源文件的访问等信息,通过自动扫描包名、注解映射支持、静态资源访问这些配置的信息在就会实例化HandlerMapping对象。这些对象是在tomcat进行参数初始化的时候也会实例化完成。在DispatcherServlet中维护着一个表,类似C#MVC中的RouteTable 路由表,存放的是HandlerMapping对象list.下面截图是DispatcherServlet中的部分代码,在DispatcherServlet中维护着handerMappings、handerAdapters等对象列表。在initStrategies中对上面的一些属性进行初始化。

        protected void initStrategies(ApplicationContext context) {
            initMultipartResolver(context);
            initLocaleResolver(context);
            initThemeResolver(context);
            initHandlerMappings(context);
            initHandlerAdapters(context);
            initHandlerExceptionResolvers(context);
            initRequestToViewNameTranslator(context);
            initViewResolvers(context);
            initFlashMapManager(context);
        }
    View Code

      2.客户端发出请求,由 Tomcat 接收到这个请求,如果匹配 DispatcherServlet 在 web.xml 中配置的映射路径,Tomcat 就将请求转交给 DispatcherServlet 处理

    二、  DispatcherServlet收到请求调用HandlerMapping处理器映射器。

      请求到达DispatcherServlet中之后,就是get、post这些请求,这些请求是DispatcherServlet的父类FrameworkServlet中定义着的,而在这些方法中又调用了processRequest,processRequest中调用了doService,DispatcherServlet重写了doService方法,doService中主要设置了一些属性和调用doDispatch方法,doDispatch用来做分发请求。

        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) {
                        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;
                    }
    
                    // Actually invoke the handler.
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }
    
                    applyDefaultViewName(processedRequest, mv);
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                }
                catch (Exception ex) {
                    dispatchException = ex;
                }
                catch (Throwable err) {
                    // As of 4.3, we're processing Errors thrown from handler methods as well,
                    // making them available for @ExceptionHandler methods and other scenarios.
                    dispatchException = new NestedServletException("Handler dispatch failed", err);
                }
                processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
            }
            catch (Exception ex) {
                triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
            }
            catch (Throwable err) {
                triggerAfterCompletion(processedRequest, response, mappedHandler,
                        new NestedServletException("Handler processing failed", err));
            }
            finally {
                if (asyncManager.isConcurrentHandlingStarted()) {
                    // Instead of postHandle and afterCompletion
                    if (mappedHandler != null) {
                        mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                    }
                }
                else {
                    // Clean up any resources used by a multipart request.
                    if (multipartRequestParsed) {
                        cleanupMultipart(processedRequest);
                    }
                }
            }
        }
    View Code

    上面的代码是核心代码,其实下面的几项其实就是代码的说明. 

    processedRequest = checkMultipart(request);
    multipartRequestParsed = (processedRequest != request);

    上面这两句是判断请求是不是上传文件的请求

    三、  处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。

    mappedHandler = getHandler(processedRequest);
    if (mappedHandler == null) {
        noHandlerFound(processedRequest, response);
        return;
    }

    getHander返回的是一个HandlerExecutionChain,这个HandlerExecutionChain中包含一个handler和多个HandlerInterceptor(拦截器),这个和asp.net中的管道模型有点类似,一个httphandler和多个httpmodule,httpmodule也是用来做拦截操作的。同时要留意HandlerExecutionChain是通过HandlerMapping对象的getHandler获取的.其实这里还可以扩展比如拦截器的使用、HandlerMapping的介绍等,由于篇幅有限,会在以后的博客中一个一个的介绍。

    四、  DispatcherServlet调用HandlerAdapter处理器适配器。

    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
        protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
            if (this.handlerAdapters != null) {
                for (HandlerAdapter ha : this.handlerAdapters) {
                    if (logger.isTraceEnabled()) {
                        logger.trace("Testing handler adapter [" + ha + "]");
                    }
                    if (ha.supports(handler)) {
                        return ha;
                    }
                }
            }
            throw new ServletException("No adapter for handler [" + handler +
                    "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
        }
    View Code

    获取适配器是通过遍历handlerAdapters列表找的,HandlerAdapter包含了3个方法.boolean supports(Object handler);ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;long getLastModified(HttpServletRequest request, Object handler);

    /*
     * Copyright 2002-2013 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.web.servlet;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.springframework.lang.Nullable;
    
    /**
     * MVC framework SPI, allowing parameterization of the core MVC workflow.
     *
     * <p>Interface that must be implemented for each handler type to handle a request.
     * This interface is used to allow the {@link DispatcherServlet} to be indefinitely
     * extensible. The {@code DispatcherServlet} accesses all installed handlers through
     * this interface, meaning that it does not contain code specific to any handler type.
     *
     * <p>Note that a handler can be of type {@code Object}. This is to enable
     * handlers from other frameworks to be integrated with this framework without
     * custom coding, as well as to allow for annotation-driven handler objects that
     * do not obey any specific Java interface.
     *
     * <p>This interface is not intended for application developers. It is available
     * to handlers who want to develop their own web workflow.
     *
     * <p>Note: {@code HandlerAdapter} implementors may implement the {@link
     * org.springframework.core.Ordered} interface to be able to specify a sorting
     * order (and thus a priority) for getting applied by the {@code DispatcherServlet}.
     * Non-Ordered instances get treated as lowest priority.
     *
     * @author Rod Johnson
     * @author Juergen Hoeller
     * @see org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter
     * @see org.springframework.web.servlet.handler.SimpleServletHandlerAdapter
     */
    public interface HandlerAdapter {
    
        /**
         * Given a handler instance, return whether or not this {@code HandlerAdapter}
         * can support it. Typical HandlerAdapters will base the decision on the handler
         * type. HandlerAdapters will usually only support one handler type each.
         * <p>A typical implementation:
         * <p>{@code
         * return (handler instanceof MyHandler);
         * }
         * @param handler handler object to check
         * @return whether or not this object can use the given handler
         */
        boolean supports(Object handler);
    
        /**
         * Use the given handler to handle this request.
         * The workflow that is required may vary widely.
         * @param request current HTTP request
         * @param response current HTTP response
         * @param handler handler to use. This object must have previously been passed
         * to the {@code supports} method of this interface, which must have
         * returned {@code true}.
         * @throws Exception in case of errors
         * @return ModelAndView object with the name of the view and the required
         * model data, or {@code null} if the request has been handled directly
         */
        @Nullable
        ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
    
        /**
         * Same contract as for HttpServlet's {@code getLastModified} method.
         * Can simply return -1 if there's no support in the handler class.
         * @param request current HTTP request
         * @param handler handler to use
         * @return the lastModified value for the given handler
         * @see javax.servlet.http.HttpServlet#getLastModified
         * @see org.springframework.web.servlet.mvc.LastModified#getLastModified
         */
        long getLastModified(HttpServletRequest request, Object handler);
    
    }
    View Code

    五、  HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。

    六、  Controller执行完成返回ModelAndView。

    七、  HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。

                    // 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;
                    }
    
                    // Actually invoke the handler.
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    View Code

     上面的几行代码先判断如果是get请求更新下lastModified请求头,然后执行HandlerExecutionChain中的applyPreHandle这个方法.

        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;
        }
    View Code
        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);
                    }
                }
            }
        }
    View Code

    在applyPreHandle中它会遍历该HandlerExecutionChain中所有的拦截器,然后使用拦截器通过preHandle对handler进行预处理,如果所有的拦截器都能处理那就会继续往下执行,如果一旦有一个拦截器不能处理,就没必要往下走了,那就会触发triggerAfterCompletion方法,在triggerAfterCompletion中它是倒序遍历的拦截器的,执行完triggerAfterCompletion返回false之后doDispatch这个方法就执行结束了,下面的八、九、十、十一就不再执行。从第五步括号里的备注也能猜出来HandlerExecutionChain.handler是什么,它可以是Controller。

    八、  DispatcherServlet将ModelAndView传给ViewReslover视图解析器。

     九、  ViewReslover解析后返回具体View。

    applyDefaultViewName(processedRequest, mv);
        private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception {
            if (mv != null && !mv.hasView()) {
                String defaultViewName = getDefaultViewName(request);
                if (defaultViewName != null) {
                    mv.setViewName(defaultViewName);
                }
            }
        }
    View Code
        protected String getDefaultViewName(HttpServletRequest request) throws Exception {
            return (this.viewNameTranslator != null ? this.viewNameTranslator.getViewName(request) : null);
        }
    View Code

    找到ModelAndView对应的viewname,设置成它的属性值。

    mappedHandler.applyPostHandle(processedRequest, response, mv);

    applyPostHandle与applyPreHandle是对应着的,遍历handler的拦截器,执行postHandle方法。

    十、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。

    processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
                @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
                @Nullable Exception exception) throws Exception {
    
            boolean errorView = false;
    
            if (exception != null) {
                if (exception instanceof ModelAndViewDefiningException) {
                    logger.debug("ModelAndViewDefiningException encountered", exception);
                    mv = ((ModelAndViewDefiningException) exception).getModelAndView();
                }
                else {
                    Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
                    mv = processHandlerException(request, response, handler, exception);
                    errorView = (mv != null);
                }
            }
    
            // Did the handler return a view to render?
            if (mv != null && !mv.wasCleared()) {
                render(mv, request, response);
                if (errorView) {
                    WebUtils.clearErrorRequestAttributes(request);
                }
            }
            else {
                if (logger.isDebugEnabled()) {
                    logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
                            "': assuming HandlerAdapter completed request handling");
                }
            }
    
            if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
                // Concurrent handling started during a forward
                return;
            }
    
            if (mappedHandler != null) {
                mappedHandler.triggerAfterCompletion(request, response, null);
            }
        }
    View Code
        protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
            // Determine locale for request and apply it to the response.
            Locale locale =
                    (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
            response.setLocale(locale);
    
            View view;
            String viewName = mv.getViewName();
            if (viewName != null) {
                // We need to resolve the view name.
                view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
                if (view == null) {
                    throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
                            "' in servlet with name '" + getServletName() + "'");
                }
            }
            else {
                // No need to lookup: the ModelAndView object contains the actual View object.
                view = mv.getView();
                if (view == null) {
                    throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
                            "View object in servlet with name '" + getServletName() + "'");
                }
            }
    
            // Delegate to the View object for rendering.
            if (logger.isDebugEnabled()) {
                logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
            }
            try {
                if (mv.getStatus() != null) {
                    response.setStatus(mv.getStatus().value());
                }
                view.render(mv.getModelInternal(), request, response);
            }
            catch (Exception ex) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
                            getServletName() + "'", ex);
                }
                throw ex;
            }
        }
    View Code

    在processDispatchResult方法中有一句render(mv, request, response),在render中先是获取View对象然后调用view.render(mv.getModelInternal(), request, response);将view和model绑定来进行渲染试图。

    十一、 DispatcherServlet响应用户。

    响应用户其实是在父类FrameworkServlet的processRequest方法中

    十二、小结

    上面黑字部分是参考其他博客的,红色的是通过读源码找的,现在基本清楚springmvc大致的流程,其实如果继续深入的话,还有好多知识点,这篇博客就写到这,以后再慢慢补充。好久没熬夜学习了,今天算是毕业之后最用功的一天了,哈哈...(今天四月一,愚人节!!!)

    参考:https://www.cnblogs.com/xiaoxi/p/6164383.html

     

  • 相关阅读:
    MonoRail学习Component
    Eclipse中用户库的使用
    ASP.Net2.0中自定义控件在page中的注册
    WF学习Console Workflow Application
    Which Style of Workflow When?[转]
    使用UserControl时,在Page中定义方式
    参数类型异常
    GridView使用一些记录
    在使用Mybatis进行测试时出现报错:Caused by: java.lang.ClassNotFoundException: Cannot find class: Student
    递归反向输出字符串
  • 原文地址:https://www.cnblogs.com/5ishare/p/8683971.html
Copyright © 2020-2023  润新知