九零后大叔
技术人的点点滴滴

springmvc篇-(二)SpringmMVC的核心工作原理一http请求在 DispatcherServlet的层层委托传递

前一章节回顾: 通过前一个章节的学习,我们知道了一个http请求是如何被web容器转交给业务Servlet的doGet或者doPost方法进行处理的了。当我们在使用springmvc框架做业务开发的时候,web容器(tomcat或者jetty)在接收到一个http请求后,会交给springmvc框架的DispatcherServlet这个业务Servlet类做业务处理。可是这个类是springmvc框架的一个类,并没有任何我们的业务代码处理,我们真正做http请求业务处理的都是在Controller类(UserController,LoginController等等)。由此我们基本上可以确认,一个http请求一定是被DispatcherServlet类转交给了各个Controller业务类。那么一个http请求又是如何被DispatcherServlet转交给我们的业务Controller类(UserController,LoginController等等)的呢?下面我们将详细的介绍这个过程


1. DispatcherServlet基本介绍

DispatcherServlet是一个Servlet的子类,是SpringMVC框架的一个关键核心类,用于将所有的http请求转发给对应的Controller处理。DispatcherServlet继承至FrameworkServlet类,FrameworkServlet类又继承至HttpServlet类,所以我们知道DispatcherServlet一定有重写doGet或者doPost方法,用于处理一个http请求。但是我们去翻看DispatcherServlet类,我们并没有找到doGet或者doPost方法,所以非常有可能是FrameworkServlet类重写了重写doGet或者doPost方法。

第一步:查看FrameworkServlet类,我们找到了如下的代码片段。这个方法会被service方法调用(参见前一章节的service方法)。

@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    //再次将http请求委托给processRequest方法处理
    processRequest(request, response);
}

第二步:将http请求交给processRequest方法处理(此代码片段还在FrameworkServlet类)

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {

    long startTime = System.currentTimeMillis();
    Throwable failureCause = null;

    LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
    LocaleContext localeContext = buildLocaleContext(request);

    RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
    ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

    initContextHolders(request, localeContext, requestAttributes);

    try {
        //再次将http请求委托给doService方法处理
        doService(request, response);
    }
    catch (ServletException ex) {
        failureCause = ex;
        throw ex;
    }
    catch (IOException ex) {
        failureCause = ex;
        throw ex;
    }
    catch (Throwable ex) {
        failureCause = ex;
        throw new NestedServletException("Request processing failed", ex);
    }

    finally {
        resetContextHolders(request, previousLocaleContext, previousAttributes);
        if (requestAttributes != null) {
            requestAttributes.requestCompleted();
        }

        if (logger.isDebugEnabled()) {
            if (failureCause != null) {
                this.logger.debug("Could not complete request", failureCause);
            }
            else {
                if (asyncManager.isConcurrentHandlingStarted()) {
                    logger.debug("Leaving response open for concurrent processing");
                }
                else {
                    this.logger.debug("Successfully completed request");
                }
            }
        }

        publishRequestHandledEvent(request, response, startTime, failureCause);
    }
}

第三步:将http请求交给doService方法处理(此代码片段在DispatcherServlet类,因为在FrameworkServlet类doService方法是一个抽象方法,该方法在DispatcherServlet子类被实现)

@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
    if (logger.isDebugEnabled()) {
        String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
        logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
                " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
    }

    // Keep a snapshot of the request attributes in case of an include,
    // to be able to restore the original attributes after the include.
    Map<String, Object> attributesSnapshot = null;
    if (WebUtils.isIncludeRequest(request)) {
        attributesSnapshot = new HashMap<String, Object>();
        Enumeration<?> attrNames = request.getAttributeNames();
        while (attrNames.hasMoreElements()) {
            String attrName = (String) attrNames.nextElement();
            if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
                attributesSnapshot.put(attrName, request.getAttribute(attrName));
            }
        }
    }

    // Make framework objects available to handlers and view objects.
    request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
    request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
    request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
    request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

    FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
    if (inputFlashMap != null) {
        request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
    }
    request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
    request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);

    try {
        //再次将http请求委托给doDispatch方法处理,doDispatch方法就是我们的核心springmvc方法了
        doDispatch(request, response);
    }
    finally {
        if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
            // Restore the original attribute snapshot, in case of an include.
            if (attributesSnapshot != null) {
                restoreAttributesAfterInclude(request, attributesSnapshot);
            }
        }
    }
}

第四步:将http请求交给doDispatch方法处理(此代码片段在DispatcherServlet类),本方法就是springmvc的核心代码了。

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);

            //1.对一个http请求,最少会有1个方法处理,最多会有1+3N个方法处理。1代表的是Controller类中的实际业务方法(例如UserController的一个findUserById业务方法),N代表的是拦截器的数量(3是因为一个拦截器最多拦截三次,一次是业务方法即将执行前,一个是业务方法刚刚执行后,一个是方法彻底完成。这个地方的思想和切面挺类似的,也是在目标方法前或者后面执行切面方法)。此处会将1+3N个业务方法打包成为一个mappedHandler对象。
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null || mappedHandler.getHandler() == null) {
                noHandlerFound(processedRequest, response);
                return;
            }

            //2.将Controller的业务方法(例如UserController的findUserById业务方法)取出来,包装成为一个HandlerAdapter对象。以备后面真正的调用该方法,用来处理http请求。
            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;
                }
            }

            //3.如果Controller的业务方法存在拦截器前置拦截方法,就先执行前置拦截方法。如果有N个拦截器,那么这N个拦截器的前置拦截方法都会被执行。
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }

            //4.执行Controller的业务方法
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

            if (asyncManager.isConcurrentHandlingStarted()) {
                return;
            }

            applyDefaultViewName(processedRequest, mv);
            //5.如果Controller的业务方法存在拦截器后置拦截方法,就先执行后置拦截方法。如果有N个拦截器,那么这N个拦截器的后置拦截方法都会被执行。
            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 afterCompletion
            if (mappedHandler != null) {
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            }
        }
        else {
            // Clean up any resources used by a multipart request.
            if (multipartRequestParsed) {
                cleanupMultipart(processedRequest);
            }
        }
    }
}


2. DispatcherServlet处理http请求全过程摘要

下面我们把前一章节和本章节的整个过程的关键代码行总结一下,我们就能了解一个http请求是如何最终被交给springmvc的DispatcherServlet类进行处理的。

    下面的这一段代码可能比较绕,一直在DispatcherServlet、FrameworkServlet、DispatcherServlet三个类之间跳来跳去。其实不复杂,不过就是父类调子类,子类又调父类,父类最后调子类的一个过程而已。

    web容器(tomcat或者jetty)调用DispatcherServlet的service方法
    [1] DispatcherServlet -> service(此处是HttpServlet类的service方法) -> service(request, response);
    [2] DispatcherServlet -> service(此处是FrameworkServlet类的service方法) -> super.service(request, response);
    [3] DispatcherServlet -> service(此处是HttpServlet类的service方法) -> doPost(req, resp);
    [4] DispatcherServlet -> doPost(此处是FrameworkServlet类的doPost方法) -> processRequest(request, response);
    [5] DispatcherServlet -> doService(此处是DispatcherServlet类的doService方法) -> doDispatch(request, response);
    doDispatch核心方法介绍
    将处理一个http请求的业务方法和各种拦截方法打包成为一个HandlerExecutionChain对象
    [5-1] DispatcherServlet-> doDispatch -> mappedHandler = getHandler(processedRequest);
    将处理一个http请求的业务方法(Controller里面与url路径对应的方法)包装成为HandlerAdapter对象
    [5-2] DispatcherServlet -> doDispatch -> HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    调用一个http请求的拦截器前置拦截方法,如果有多个拦截器会在这里一并调用
    [5-3] DispatcherServlet -> doDispatch -> !mappedHandler.applyPreHandle(processedRequest, response)
    通过调用ha.handle的方式间接的调用业务方法(Controller里面与url路径对应的方法),至于细节本章节暂时不讨论,下一章节会细细讨论
    [5-4] DispatcherServlet -> doDispatch -> mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    调用一个http请求的拦截器后置拦截方法,如果有多个拦截器会在这里一并调用
    [5-5] DispatcherServlet -> doDispatch -> mappedHandler.applyPostHandle(processedRequest, response, mv);
    调用一个http请求的拦截器完成拦截方法,如果有多个拦截器会在这里一并调用
    [5-6] DispatcherServlet -> doDispatch -> processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

注:DispatcherServlet继承至FrameworkServlet,FrameworkServlet继承至DispatcherServlet


通过前面两小节的了解,我们基本上对于一个http请求是如何被一步一步的转交给我们自己的Controller类的业务方法处理的了。

更多知识请关注公众号

赞(0) 赏一下
未经允许不得转载:九零后大叔的技术博客 » springmvc篇-(二)SpringmMVC的核心工作原理一http请求在 DispatcherServlet的层层委托传递
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

九零后大叔的技术博客

联系我们联系我们

感谢您的支持

支付宝扫一扫打赏

微信扫一扫打赏