转载

SpringMVC请求处理流程

Spring原码解析—SpringMVC请求处理流程

我们都知道SpringMVC的核心就是DispatcherServlet,它负责接收请求和调度,在开始原码解析前先来看看SpringMVC请求的一个处理流程图:

1)客户端发送请求,DispatcherServlet接收请求

2)DispatcherServlet将请求交给DefaultAnnotationHandlerMapping找到对应的映射方法

3)通过对应的DefaultAnnotationHandlerAdapter invoke特定的方法

4)调用自己的Controller和相应的方法,给Model设置数据和返回视图

5)DefaultAnnotationHandlerAdapter组装ModelAndView并返回给DispatcherServlet

6)通过InternalResourceViewResolver解析

7)响应客户端


对请求流程有了一个初步的了解后,接下来我们开始原码分析:

确定请求入口:

因为DispatcherServlet本质上是一个Servlet所有它一定有doPost()和doGet()方法,而DispatcherServlet又继承FrameworkServlet,通过分析我们在FrameworkServlet中找到请求处理方法,如图:

而doPost()和doGet()方法又会调用pocessRequest()这个方法,如图:

file

因此我们在processRequest这个方法上打上断点。

接下来开启debug并发送请求,来到断点处processRequest方法,其原码如下:

    //处理请求
    protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        long startTime = System.currentTimeMillis();
        Throwable failureCause = null;
        //获取LocaleContextHolder中原来保存的LocaleContext
        //刚开始一般是null
        LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
        //获取当前请求的LocaleContext
        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());
   //将当前请求的LocaleContext和ServletRequestAttributes设置到LocaleContextHolder和RequestContextHolder中
        initContextHolders(request, localeContext, requestAttributes);

        try {

            //前面都可以不太了解,重点是这个方法,执行完前面的操作后,会将请求统一交给这个方法处理逻辑
            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();
                //这里执行了request后有一个requestActive状态值会转变为false,执行前是true

            }

            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");
                    }
                }
            }
             //发布ServletRequestHandlerEvent消息,这个请求是否执行成功都会发布消息的
            publishRequestHandledEvent(request, response, startTime, failureCause);
        }
    }

我们在来看一下doService这个方法,这个方法被DispatcherServlet覆写了,所有调用的是DispatcherServlet的实现:

DispatcherServlet.java

        @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(DEFAULT_STRATEGIES_PREFIX)) {
                    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 {
                //实际上,执行完一系列操作它又会调用这个方法,我们跟踪进入
            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);
                }
            }
        }
    }

在看原码的时候,我们首先不应该关注太多的细节,而应掌握它的主要流程。

我们接着看一下doDispatch()这个方法:

DispatcherServlet.java

    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.
                //通过我们请求的路径 拿到需要访问的自定义的Controller里的指定的方法    包含请求和拦截器
                mappedHandler = getHandler(processedRequest);  //-----》HandlerExecutionChain -》mappedHandler
                if (mappedHandler == null || mappedHandler.getHandler() == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // Determine handler adapter for the current request.
                //通过handler拿到对应的handlerAdapter  我们这里是RequestMappingHandlerAdapter
                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.
                //通过反射调用 我们那个方法  并通过RequestMappingHandlerAdapter组装ModelAndView 也就是将return的String与Model组装
                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);
                }
            }
        }
    }

在执行完processDispatchResult后就差不多完成了我们的流程

我们看一下这个方法:

file

最后在看一下publishRequestHandledEvent这个事件发布方法的内容。

private void publishRequestHandledEvent(
    HttpServletRequest request, HttpServletResponse response, long startTime, Throwable failureCause) {

    if (this.publishEvents) {
        // Whether or not we succeeded, publish an event.
        long processingTime = System.currentTimeMillis() - startTime;
        int statusCode = (responseGetStatusAvailable ? response.getStatus() : -1);
        this.webApplicationContext.publishEvent(
                new ServletRequestHandledEvent(this,
                        request.getRequestURI(), request.getRemoteAddr(),
                        request.getMethod(), getServletConfig().getServletName(),
                        WebUtils.getSessionId(request), getUsernameForRequest(request),
                        processingTime, failureCause, statusCode));
    }
}

在publishRequestHandledEvent方法执行后,如果有自定义实现ApplicationListener,那么就会调用里面的onApplicationEvent方法,如图:

import org.springframework.web.context.support.ServletRequestHandledEvent;

@Component
public class ServletReqestHandledEventListener implements ApplicationListener<ServletRequestHandledEvent> {
    final static Logger logger = LoggerFactory.getLogger("SRHEServlet");

    @Override
    public void onApplicationEvent(ServletRequestHandledEvent servletRequestHandledEvent) {
        logger.info(servletRequestHandledEvent.getDescription()+"自定义ApplicationListener");
    }
}

当请求结束后,控制台打印:

file

它可以用来记录日志,并且只需要把自己需要做的事情写到onApplicationEvent里面就可以了。
总结:processRequest -> doService -> doDispatch -> preHandle..post..after ...
->processDispatchResult ->publishRequestHandledEvent
file

正文到此结束
本文目录