详解spring-MVC DispatcherServlet运行

HttpServletBean

HttpServletBean主要参与了创建工作,并没有涉及请求的处理。

FrameworkServlet

在FrameworkServlet中重写了service,doGet,doPost(除了doHead以外的所有请求处理方法)

#

protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //得到是何种方法
        String method = request.getMethod();
        if(method.equalsIgnoreCase(RequestMethod.PATCH.name())) {

            this.processRequest(request, response);
        } else {
            super.service(request, response);
        }

    }
     protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.processRequest(request, response);
    }

在这里跟HttpServlet不同,将不同类型的请求路由到不同方法进行处理的思路正好相反,这里又将所有的请求合并到了processRequest方法。
接下来看processRequest方法

protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        long startTime = System.currentTimeMillis();
        Object failureCause = null;
        //获取LocaleContext
        LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
        //获取当前请求的LocaleContext
        LocaleContext localeContext = this.buildLocaleContext(request);
        //获取RequestAttributes
        RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
        //获取当前请求的ServletRequestAttributes
        ServletRequestAttributes requestAttributes = this.buildRequestAttributes(request, response, previousAttributes);
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new FrameworkServlet.RequestBindingInterceptor(null));
        //把当前请求的LocaleContext和ServletRequestAttributes设置到LocaleContextHolder和RequestContextHolder
        this.initContextHolders(request, localeContext, requestAttributes);

        try {
            //实际处理
            this.doService(request, response);
        } catch (ServletException var17) {
            failureCause = var17;
            throw var17;
        } catch (IOException var18) {
            failureCause = var18;
            throw var18;
        } catch (Throwable var19) {
            failureCause = var19;
            throw new NestedServletException("Request processing failed", var19);
        } finally {
            //恢复原来的
            this.resetContextHolders(request, previousLocaleContext, previousAttributes);
            if(requestAttributes != null) {
                requestAttributes.requestCompleted();
            }

            if(this.logger.isDebugEnabled()) {
                if(failureCause != null) {
                    this.logger.debug("Could not complete request", (Throwable)failureCause);
                } else if(asyncManager.isConcurrentHandlingStarted()) {
                    this.logger.debug("Leaving response open for concurrent processing");
                } else {
                    this.logger.debug("Successfully completed request");
                }
            }
            //发布了ServletRequestHanledEvent消息。
            this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause);
        }

    }

在这个方法之中doService是核心方法,在DispatcherServlet中具体实现。doService前后还做了写事情,装修者模式。主要是做了两件事:
1.对LocaleContext(本地消息)和RequestAttributes(属性获取用于管理request和session)的设置及恢复;
2.处理完后发布了ServletRequestHandledEvent消息。

DispatcherServlet

经过上面的分析我们的代码在doService()中,接下来分析

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

        HashMap attributesSnapshot1 = null;
        if(WebUtils.isIncludeRequest(request)) {
            attributesSnapshot1 = new HashMap();
            Enumeration inputFlashMap = request.getAttributeNames();

            while(inputFlashMap.hasMoreElements()) {
                String attrName = (String)inputFlashMap.nextElement();
                if(this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
                    attributesSnapshot1.put(attrName, request.getAttribute(attrName));
                }
            }
        }
        //对request设置属性
        request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());
        request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
        request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
        request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource());
        FlashMap inputFlashMap1 = this.flashMapManager.retrieveAndUpdate(request, response);
        if(inputFlashMap1 != null) {
            request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap1));
        }

        request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
        request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);

        try {
            //真正执行的地方
            this.doDispatch(request, response);
        } finally {
            if(!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot1 != null) {
                this.restoreAttributesAfterInclude(request, attributesSnapshot1);
            }

        }

    }

我们首先会设置webApplicationContext,localResolver,themeResolver和themeSource在之后介绍的handler和view中需要使用,到时候再作分析。后面三个属性都和flashMap有关,主要是用于重定向的参数和传递。一般我们重定向的时候传参数过去只有靠url的重写,但是这里我们可以使用flushMap向其中传递我们需要传递的参数然后就可以进行重定向的传参了。
接下来对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 {
            try {
                ModelAndView err = null;
                Exception dispatchException = null;

                try {
                    processedRequest = this.checkMultipart(request);
                    multipartRequestParsed = processedRequest != request;
                    mappedHandler = this.getHandler(processedRequest);
                    if(mappedHandler == null || mappedHandler.getHandler() == null) {
                        this.noHandlerFound(processedRequest, response);
                        return;
                    }

                    HandlerAdapter ex = this.getHandlerAdapter(mappedHandler.getHandler());
                    String method = request.getMethod();
                    boolean isGet = "GET".equals(method);
                    if(isGet || "HEAD".equals(method)) {
                        long lastModified = ex.getLastModified(request, mappedHandler.getHandler());
                        if(this.logger.isDebugEnabled()) {
                            this.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;
                    }

                    err = ex.handle(processedRequest, response, mappedHandler.getHandler());
                    if(asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }

                    this.applyDefaultViewName(request, err);
                    mappedHandler.applyPostHandle(processedRequest, response, err);
                } catch (Exception var19) {
                    dispatchException = var19;
                }

                this.processDispatchResult(processedRequest, response, mappedHandler, err, dispatchException);
            } catch (Exception var20) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, var20);
            } catch (Error var21) {
                this.triggerAfterCompletionWithError(processedRequest, response, mappedHandler, var21);
            }

        } finally {
            if(asyncManager.isConcurrentHandlingStarted()) {
                if(mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            } else if(multipartRequestParsed) {
                this.cleanupMultipart(processedRequest);
            }

        }
    }

上面是大概的代码其实把所有代码精简了之后缩略为4句:
1.mappedHandler = this.getHandler(processedRequest);
2.HandlerAdapter ex = this.getHandlerAdapter(mappedHandler.getHandler());
3.err = ex.handle(processedRequest, response, mappedHandler.getHandler());
4.this.processDispatchResult(processedRequest, response, mappedHandler, err, dispatchException);
上面4句话概括为:
1.根据request找到Handler。
2.根据Handler找到对应的HandlerAdapter
3.用HandlerAdapter处理Handler
4.调用processDispatchResult处理方法结果。
这里需要解释三个概念:HandlerMapping,Handler和HandlerAdapter。
Handler:也就是处理器,它直接对应着MVC中的C层也就是controller层,它的具体表现形式有很多,可以是类,也可以是方法,如果你能想到别的表现形式也可以使用。类型是Object。对于注解中我们只要标注了@RequestMapping的所有方法都可以看成一个Handler。只要可以处理实际请求就可以是Handler。
HandlerMapping:用来查找Handler的,在Spring MVC中会处理很多请求,每个请求都需要一个Handler处理,我们接收到请求以后使用哪个Handler来处理呢?这就是HandlerMapping需要来告诉我的。
HandlerAdapter:很多人对这个理解都比较模糊,其实从名字上可以看出他是一个Adapter,也就是适配器。因为Spring MVC中的Handler可以是任意形式,只要能处理请求就OK,但是Servlet需要的处理方法的结构是固定的,都是以request和response为参数,这个时候就需要一个适配器来处理这个Handler。

doDispatch总共可以分成4句话,我们也可以把它分为两部分:处理请求和渲染页面。
在整个程序流程开始的时候我们会有如下的定义

//请求,如果不是上传请求则直接使用接收到的request,否则进行处理
HttpServletRequest processedRequest = request;
//处理请求链,包含我们的拦截器和我们处理器。
HandlerExecutionChain mappedHandler = null;
//上传文件标志位
boolean multipartRequestParsed = false;
//封装Model和View
ModelAndView err = null;
//处理请求过程中抛出的异常
Exception dispatchException = null;

具体步骤
1。doDispatch中首先检查是不是上传请求判断是否是Post并且content-Type是否以”multipart/”开头,如果是则会进行处理。会把文件和对应的名字封装成map。
2。通过getHandler方法获取Handler处理器链,原理是使用HandlerMapping。举一个例子来说:对于我们的方法处理器Mapping来说 会通过我们的AbstractHandlerMethodMapping首先通过我们的URL找到了我们的匹配条件,然后根据我们的匹配条件这里的匹配条件不止是URL匹配这么简单也包括我们get,post方法请求和请求参数等。利用匹配条件找到了Handler。
3。然后使用HandlerAdapter适配器处理我们的请求。
4。处理异常,渲染页面。

    原文作者:Spring MVC
    原文地址: https://blog.csdn.net/li563868273/article/details/50991989
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞