SpringMVC源代码学习(三)DispatcherServlet

本文基于《看透SpringMVC-源代码分析与实践》深入探讨DispatcherServlet的工作原理。DispatcherServlet作为SpringMVC的核心,主要职责包括:判断并处理include请求,设置request属性,调用doDispatch方法,以及在必要时还原request备份。doDispatch中涉及HandlerMapping查找Handler,HandlerAdapter适配处理方法,整个处理流程清晰地展示了请求从接收、映射到处理的完整过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

以下内容基于书:《看透SpringMVC-源代码分析与实践》基本照搬。。。用于自己查阅备忘。

DispatcherServlet是 SpringMVC最核心的类。
上文提到对请求的处理传递到了DispatcherServlet的,
代码如下:

DispatcherServlet(doService)

@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
    //log ...

    //建立一个attributesSnapshot用于备份
    Map<String, Object> attributesSnapshot = null;
    // 当request是一个include request时
    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));
            }
        }
    }

    //对request设置一些属性
    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 {
        //执行处理请求的主方法
        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);
            }
        }
    }
}

总的来说做的事:
1、判断是不是include请求?对request的Attribute做个快照备份:do nothing;
2、对request设置了一些属性。
这里request.setAttribute前四个属性与handler和view有关,后文再分析。
后面三个属性同FlashMap有关
对FlashMap的介绍可以看我的另一篇博客
3、doDispatch(request,response);
4、如果做了快照,进行还原。

下面来看一下doDispatch,源码如下:

DispatcherServlet(doService)

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;//processedRequest 是在本方法中实际处理的request对象
        HandlerExecutionChain mappedHandler = null;//非常重要的Interceptor
        boolean multipartRequestParsed = false;

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            ModelAndView mv = null;
            Exception dispatchException = null;

            try {
                processedRequest = checkMultipart(request);//1、检查是不是上传文件请求
                multipartRequestParsed = (processedRequest != request);//是的话留下标记


                // Determine handler for the current request.
                mappedHandler = getHandler(processedRequest);//2、根据request找到HandlerExecutionChain
                if (mappedHandler == null || mappedHandler.getHandler() == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // Determine handler adapter for the current request.
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());//3、根据handler找到handlerAdapter

                // Process last-modified header, if supported by the handler.
                String method = request.getMethod();  //4、处理是否被修改字段,下文详细解释
                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)) {//5、mappedHandler内的interceptor 对request 进行preHandle
                    return;
                }

                // Actually invoke the handler.
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());//6、 实际处理请求发生的地方

                //如果需要异步处理,直接返回
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }
                applyDefaultViewName(processedRequest, mv);//7、处理view,若view为空,根据request设置默认view
                mappedHandler.applyPostHandle(processedRequest, response, mv);//8、interceptor 对request 进行postHandle
            }
            catch (Exception ex) {
                dispatchException = ex;
            }

            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);//如果捕捉到Exception,调用该方法进行处理,注意下方还有外层catch捕捉该方法throw的exception
        }
        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);//8、如果是上传请求,最后清空生成的临时资源
                }
            }
        }
    }

Last-Modeified:浏览器第一次跟服务器请求资源(GET,Head请求)时,服务器在返回的请求头里面会包含一个Last-Modified的属性,代表资源什么时候修改的,浏览器之后发送请求会发送之前接受到的Last-Modified。服务器接收到Last-Modified后会和自己实际资源的修改时间对比。过期了就返回新的资源和新的Last-Modified。否则直接返回304状态码,浏览器直接使用之前缓存的结果。

Handler:处理器,对应Controller层,具体表象形式很多,是Object类型。
只要可以实际处理请求就可以试Handler
HandlerMapping:用来查找HandlerExecutionChain,HandlerExecutionChain内存放了hadnler。因此该对象的功能是根据不同的请求获得不同的Handler。
在doDispatch方法中看不到,DispatcherServlet内有一个list属性List< HandlerMapping> handlerMappings
在获取HandlerExecutionChain的方法getHandler中遍历它获得结果。
HandlerAdapter: Handler表现形式很多(Object),而servlet需要的处理方法结构是以request,response为参数的方法,如doService。
HandlerAdapter就是完成这个功能的转换的。(具体的这里没提,有点看不懂)

通俗讲Handler是类似锄头斧头这样的工具,HandlerAdapter是用工具的人,不同工具要交给不同的人用。HandlerMapping负责完成这一分配过程。

因此处理流程可以理解为:
用HandlerMapping找到负责处理请求的Handler。
利用DispatcherServlet内部的方法,根据Handler获取使用Handler的HandlerAdapter。
HandlerAdapter使用Handler处理请求。
生成ModelAndView,返回给用户。

异常处理结构: doDispatch有两层异常捕获,内层捕获处理请求的异常,由processDispatchResult处理。
外层处理processDispatchResult方法抛出的异常。渲染页面时抛出的异常。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值