SpringMVC处理-源码跟踪

之前分析了,SpringMVC启动-源码跟踪,这里分析下SpringMVC是如何处理请求的。我们知道Servlet的处理请求的入口方法是:

public void service(ServletRequest req, ServletResponse res);

以一个Http get请求为例,绘制如下流程图:
这里写图片描述

  • 首先请求的入口是HttpServlet#service(ServletRequest req, ServletResponse res);
  • 然后HttpServlet,将请求参数转换为HttpServletRequest ,HttpServletResponse ,然后调用service(HttpServletRequest req, HttpServletResponse resp),并通过请求类型转发给不同类型的处理方法。
//javax.servlet.http.HttpServlet
 public void service(ServletRequest req, ServletResponse res)
        throws ServletException, IOException
    {
        HttpServletRequest  request;
        HttpServletResponse response;

        if (!(req instanceof HttpServletRequest &&
                res instanceof HttpServletResponse)) {
            throw new ServletException("non-HTTP request or response");
        }

        request = (HttpServletRequest) req;
        response = (HttpServletResponse) res;

        service(request, response);
    }

protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        String method = req.getMethod();
        if (method.equals(METHOD_GET)) {
            //省略....
             doGet(req, resp);
         } else if (method.equals(METHOD_HEAD)) {
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);

        } //省略其他类型请求......    
        else {
            //异常处理
            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
        }
    }
  • 接上面根据不同的请求类型,交由各类型对应的方法,由于FrameworkServlet重写了doGet()等方法,所以交由FrameworkServletdoGet()处理,然后由交由processRequest()继续后续处理。
    //org.springframework.web.servlet.FrameworkServlet
    protected final void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }
  • FrameworkServletprocessRequest()处理后,交由doService()继续处理。
    protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
            throws Exception;
  • 上述doService()方法是个abstract方法,所以这里由DispatcherServlet处理,处理完毕后交由doDispatch()进行结果返回。

请求的大致流程已经分析完毕,其中HttpServlet的处理比较简单,下面主要分析下FrameworkServletDispatcherServlet


FrameworkServlet

FrameworkServlet 重写了doGet,doPost,doPut,doDelete,doTrace等方法。并且将这些方法全部上交给processRequest()处理。

processRequest方法
processRequest是一个final方法,这说明它不允许被重写。

//org.springframework.web.servlet.FrameworkServlet
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

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

        //获取当前请求(线程)的LocaleContext
        LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
        //重新设置当前请求的LocaleContext
        LocaleContextHolder.setLocaleContext(buildLocaleContext(request), this.threadContextInheritable);

        //获取当前请求的RequestAttributes 
        RequestAttributes previousRequestAttributes = RequestContextHolder.getRequestAttributes();

        ServletRequestAttributes requestAttributes = null;
        if (previousRequestAttributes == null || previousRequestAttributes.getClass().equals(ServletRequestAttributes.class)) {
            //将RequestAttributes转换为ServletRequestAttributes
            requestAttributes = new ServletRequestAttributes(request);

            //重新设置当前请求的requestAttributes
            RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
        }


        //try-catch省略...

        //实际处理入口
        doService(request, response);

        finally {
            // 恢复原来的previousLocaleContext
            LocaleContextHolder.setLocaleContext(previousLocaleContext, this.threadContextInheritable);
            if (requestAttributes != null) {
                //恢复原来的previousRequestAttributes
                RequestContextHolder.setRequestAttributes(previousRequestAttributes, this.threadContextInheritable);
                //清空requestAttributes
                requestAttributes.requestCompleted();
            }

            //省略....
            if (this.publishEvents) {
                //无论成功与否,发布ServletRequestHandledEvent消息。
                long processingTime = System.currentTimeMillis() - startTime;
                this.webApplicationContext.publishEvent(
                        new ServletRequestHandledEvent(this,
                                request.getRequestURI(), request.getRemoteAddr(),
                                request.getMethod(), getServletConfig().getServletName(),
                                WebUtils.getSessionId(request), getUsernameForRequest(request),
                                processingTime, failureCause));
            }
        }
    }

requestAttributes.requestCompleted()

//org.springframework.web.context.request.AbstractRequestAttributes 

/**
 * 标志请求结束
 * <p>Executes all request destruction callbacks and updates the
 * session attributes that have been accessed during request processing.
 */
public void requestCompleted() {
    //执行request拆除回调函数,并清空这些回调信息。
    executeRequestDestructionCallbacks();
    //更新session中所有受影响的属性
    updateAccessedSessionAttributes();
    //设置request无效
    this.requestActive = false;
}


private void executeRequestDestructionCallbacks() {
    synchronized (this.requestDestructionCallbacks) {
        for (Runnable runnable : this.requestDestructionCallbacks.values()) {
            runnable.run();
        }
        this.requestDestructionCallbacks.clear();
    }
}

ServletRequestHandledEvent
无论成功与否,最后都会发布ServletRequestHandledEvent事件,可以自定义ApplicationListener进行监听。注意需要将该Listener注册到spring容器中

public class ServletRequestHandledEventListener implements ApplicationListener<ServletRequestHandledEvent> {

    @Override
    public void onApplicationEvent(ServletRequestHandledEvent event) {
        System.out.println(event.getDescription());
    }
}

DispatcherServlet

DispatcherServlet是SpringMVC 最核心的类,整个处理过程的顶层设计都在这里。经过上面的分析,可以得知DispatcherServlet 方法的入口是:doService()

doservice()

//org.springframework.web.servlet.DispatcherServlet
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {

        //如果是<jsp:include>include请求,快照记录当前的attributes,并在处理完成之后进行还原。
        Map<String, Object> attributesSnapshot = null;
        if (WebUtils.isIncludeRequest(request)) {
            logger.debug("Taking snapshot of request attributes before include");
            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));
                }
            }
        }

        //设置flashMap相关属性,主要用于Redirect转发时参数的传递。
        this.flashMapManager.requestStarted(request);

        // 对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());

        try {
            doDispatch(request, response);
        }
        finally {
            //"output" FlashMap保存在底层存储,并开始进行倒计时。默认180s.
            this.flashMapManager.requestCompleted(request);

            // 当时include请求时,恢复之前备份的属性
            if (attributesSnapshot != null) {
                restoreAttributesAfterInclude(request, attributesSnapshot);
            }
        }
    }

FlashMap的作用:是在redirect请求时传递参数,
outputFlashMap:用于保存本次请求需要转发的属性。
inputFlashMap:用于保存上次请求转发过来的属性。
它的使用方式如下:

@RequestMapping("/submit")
    public String submit(RedirectAttributes attr) {
        //获取FlashMap方法1
        ((FlashMap) ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                .getRequest().getAttribute(FlashMapManager.OUTPUT_FLASH_MAP_ATTRIBUTE)).put("name", "特朗普传");

        //获取FlashMap方法2:RedirectAttributes.addFlashAttribute::参数放到outputFlashMap
        attr.addFlashAttribute("orderId", "9901");

        //获取FlashMap方法3:RedirectAttributes.addAttribute ::参数会拼接到url后面。
        attr.addAttribute("price", "zh-cn");
        return "redirect:detail";
    }

    @RequestMapping("/detail")
    public String detail(Model model) {
        System.out.println(model.asMap());
        return "success";
    }

doService主要对request设置了一些属性之后,最终将请求转发给doDispatch()方法。


doDispatch()结构
doDispatch的方法也非常简洁,从顶层设计了整个请求的处理过程。doDispatch的最核心代码只有4句,它们的任务分别是:

  1. 根据request找到Handler
  2. 根据Handler找到对应的HandlerAdapter
  3. 用HandlerAdapter处理Handler
  4. 处理上面处理之后的结果(包含找到View并渲染给用户),最终调用afterCompletion

对应代码如下:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    //确定当前请求的Handler
    mappedHandler = getHandler(processedRequest, false);

    //确定当前Handler的HandlerAdapter .
    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

    //HandlerAdapter 执行Handler
    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

    //触发整个请求处理完毕回调方法afterCompletion  
    triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
}

HandlerMapping :是用来寻找Handler的,可以理解为请求
Handler:也就是处理器,可以理解为Controller层。可以是类,也可是是方法。
HandlerAdapter:适配器。Handler处理请求可以是任意形式的,但是Servlet处理方法却是固定形式的,都是以request,response为参数的方法,怎么让Servlet调用灵活的Hander来进行处理呢,HandlerAdapter就是干这个事的。

引用一张图
这里写图片描述

doDispatch()详解

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        int interceptorIndex = -1;

        try {
            ModelAndView mv;
            boolean errorView = false;

            try {
                //检查是不是上传请求,如果是转换为MultipartHttpServletRequest
                processedRequest = checkMultipart(request);

                //根据request找到Handler
                mappedHandler = getHandler(processedRequest, false);
                if (mappedHandler == null || mappedHandler.getHandler() == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                //根据Handler找到HandlerAdapter
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // 处理GET,HEAD请求的last-modified header
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }

                //执行Handler注册的interceptors中的preHandle方法
                HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
                if (interceptors != null) {
                    for (int i = 0; i < interceptors.length; i++) {
                        HandlerInterceptor interceptor = interceptors[i];
                        if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
                            triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
                            return;
                        }
                        interceptorIndex = i;
                    }
                }

                // HandlerAdapter使用Handler处理请求
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                //当View为空时(比如放回方法是Void时),根据request设置默认View
                if (mv != null && !mv.hasView()) {
                    mv.setViewName(getDefaultViewName(request));
                }

                ////执行Handler注册的interceptors中的postHandle方法(逆序)
                if (interceptors != null) {
                    for (int i = interceptors.length - 1; i >= 0; i--) {
                        HandlerInterceptor interceptor = interceptors[i];
                        interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
                    }
                }
            }
            catch (ModelAndViewDefiningException ex) {
                logger.debug("ModelAndViewDefiningException encountered", ex);
                mv = ex.getModelAndView();
            }
            catch (Exception ex) {
                Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
                //处理异常请求。
                mv = processHandlerException(processedRequest, response, handler, ex);
                errorView = (mv != null);
            }

            //渲染视图。
            if (mv != null && !mv.wasCleared()) {
                render(mv, processedRequest, response);
                if (errorView) {
                    WebUtils.clearErrorRequestAttributes(request);
                }
            }
            else {
                //省略log....
            }

            // 触发整个请求处理完毕回调方法afterCompletion  
            triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
        }//省略try-catch


        finally {
            //删除上传请求的资源
            if (processedRequest != request) {
                cleanupMultipart(processedRequest);
            }
        }
    }

拆分上述方法,
getHandler()
getHandler()找到请求的HandlerExecutionChain ,执行过程按照顺序执行preHandler方法,返回时逆序执行postHandler.

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    for (HandlerMapping hm : this.handlerMappings) {
        //省略log...

        //寻找到HandlerMapping匹配到的第一个HandlerExecutionChain。
        HandlerExecutionChain handler = hm.getHandler(request);
        if (handler != null) {
            return handler;
        }
    }
    return null;
}

GET,HEAD请求的Last-Modified
当浏览器第一次跟服务器请求资源(GET,HEAD请求)时,服务器返回的响应头中包含一个Last-Modified属性,代表本资源最后是什么时候修改的。在浏览器以后发送请求时会同时发送之前接收到Last-Modified,服务器接收到带有Last-Modified的请求时会用其值和自己实际资源修改时间作比较,如果过期了则返回新的资源(同时返回新的Last-Modified),否则返回304状态码表示资源未过期,浏览器直接使用之前缓存的结果。

triggerAfterCompletion()

private void triggerAfterCompletion(HandlerExecutionChain mappedHandler,
            int interceptorIndex,
            HttpServletRequest request,
            HttpServletResponse response,
            Exception ex) throws Exception {

        // 触发整个请求处理完毕,回调interceptor的afterCompletion方法 .
        if (mappedHandler != null) {
            HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
            if (interceptors != null) {
                for (int i = interceptorIndex; i >= 0; i--) {
                    HandlerInterceptor interceptor = interceptors[i];
                    try {
                        interceptor.afterCompletion(request, response, mappedHandler.getHandler(), ex);
                    }
                    catch (Throwable ex2) {
                        logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
                    }
                }
            }
        }
    }

猜你喜欢

转载自blog.csdn.net/it_freshman/article/details/81365000