Spring MVC 源码分析之 DispatcherServl

2020-11-30  本文已影响0人  瞎胡扯1

一、概述

MVC大家比较熟悉

二、类图

image image.gif

如上图类的继承关系可知,DispatcherServlet就是一个Servlet.

三、初始化过程

了解Servlet的都知道在Servlet中主要的方法有:

3.1、HttpServletBean类初始化方法 init

public final void init() throws ServletException {

        // Set bean properties from init parameters.
        PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
        if (!pvs.isEmpty()) {
            try {
                //将Servlet中配置的参数封装到pvs变量中,requiredProperties为必须参数,如果没
                //配置将报异常
                BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
                ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
                bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));

                 //模板方法,可以在子类中实现,做一些初始化工作,
                initBeanWrapper(bw);

                bw.setPropertyValues(pvs, true);
            }
            catch (BeansException ex) {
                if (logger.isErrorEnabled()) {
                    logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
                }
                throw ex;
            }
        }

        // Let subclasses do whatever initialization they like.
        // 模板方法,子类初始化入口方法,在子类中可以做一些具体的实现方法
        initServletBean();
    }
image.gif

3.2、在 FrameworkServlet 中实现了 initServletBean 方法

@Override
    protected final void initServletBean() throws ServletException {
        // 略 .....
        try {

            //主要方法,主要实现了 初始化 WebApplicationContext 
            this.webApplicationContext = initWebApplicationContext();
            initFrameworkServlet();
        }
        catch (ServletException | RuntimeException ex) {
            logger.error("Context initialization failed", ex);
            throw ex;
        }
         // 略 .....
        }
    }
image.gif

3.3、initWebApplicationContext 方法

protected WebApplicationContext initWebApplicationContext() {

      //  略...

        if (!this.refreshEventReceived) {
            // Either the context is not a ConfigurableApplicationContext with refresh
            // support or the context injected at construction time had already been
            // refreshed -> trigger initial onRefresh manually here.
            synchronized (this.onRefreshMonitor) {
                onRefresh(wac);  //重点方法
            }
        }

        if (this.publishContext) {
            // Publish the context as a servlet context attribute.
            String attrName = getServletContextAttributeName();
            getServletContext().setAttribute(attrName, wac);
        }

        return wac;
    }
image.gif

initWebApplicationContext方法做了三件事:

  1. 获取spring的根容器rootContext。
  2. 设置webApplicationContext并根据情况调用onRefresh方法。
  3. 将webApplicationContext设置到ServletContext中。

3.4、DispatcherServlet中的 onRefresh 方法

@Override
    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }

    /**
     * Initialize the strategy objects that this servlet uses.
     * <p>May be overridden in subclasses in order to initialize further strategy objects.
     */
    protected void initStrategies(ApplicationContext context) {

        //初始化多媒体解析器,这个不分析
        initMultipartResolver(context);
      //初始化国际化解析器,这个不进行分析
        initLocaleResolver(context);
      //初始化主题解析器,基本不用,不进行分析
        initThemeResolver(context);
      //初始化映射器
        initHandlerMappings(context);
      //初始化适配器
        initHandlerAdapters(context);
      //初始化异常解析器
        initHandlerExceptionResolvers(context);
      //初始化视图名转换器,不进行分析
        initRequestToViewNameTranslator(context);
      //初始化视图解析器,前端分离后,用得少了,也分析下
        initViewResolvers(context);
      //初始化FlashMapManager,只知道是重定向时用来保存数据用的,没有使用过,这里不进行分析
        initFlashMapManager(context);
    }
image.gif

下面贴的代码的几个初始化代码的逻辑其实是一样的,分为三步,以initHandlerMappings进行说明

  1. 判断是否寻找所有容器中实现HandlerMapping接口的,是则寻找所有容器中实现了HandlerMapping接口的对象,设置到handlerMappings对象中并排序(平常开发中走的都是这个分支)
  2. 否的话,只找寻到前容器实现了HandlerMapping接口的对象
  3. 如果上面查找后为空的话,则加载配置文件中的类并实例化,再设置到handlerMappings中。注意的是:这个配置文件位置是:org/springframework/web/servlet/DispatcherServlet.properties

至此,DispatcherServlet的初始化工作已经完成。

四、请求过程

上文介绍过 Servlet中处理请求的为 service 方法。下面先看service方法的处理逻辑

4.1、HttpServlet中的 service方法

 protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {

        String method = req.getMethod();

        if (method.equals(METHOD_GET)) {
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
                // servlet doesn't support if-modified-since, no reason
                // to go through further expensive logic
                doGet(req, resp);
            } else {
                long ifModifiedSince;
                try {
                    ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                } catch (IllegalArgumentException iae) {
                    // Invalid date header - proceed as if none was set
                    ifModifiedSince = -1;
                }
                if (ifModifiedSince < (lastModified / 1000 * 1000)) {
                    // If the servlet mod time is later, call doGet()
                    // Round down to the nearest second for a proper compare
                    // A ifModifiedSince of -1 will always be less
                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else {
                    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                }
            }

        } else if (method.equals(METHOD_HEAD)) {
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);

        } else if (method.equals(METHOD_POST)) {
            doPost(req, resp);

        } else if (method.equals(METHOD_PUT)) {
            doPut(req, resp);

        } else if (method.equals(METHOD_DELETE)) {
            doDelete(req, resp);

        } else if (method.equals(METHOD_OPTIONS)) {
            doOptions(req,resp);

        } else if (method.equals(METHOD_TRACE)) {
            doTrace(req,resp);

        } else {
            //
            // Note that this means NO servlet supports whatever
            // method was requested, anywhere on this server.
            //

            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[1];
            errArgs[0] = method;
            errMsg = MessageFormat.format(errMsg, errArgs);

            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
        }
    }
image.gif

从源码中可得知 HttpServlet中 service 方法根据请求方法,把请求具体交给了 doGet、doPost、doPut等方法来处理

4.2、FrameworkServlet 中的 service 方法

在FrameworkServlet中重写了 service 方法同时也重写了 doGet、doPost,doPut等方法,源码如下所示

@Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
        if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
            processRequest(request, response);
        }
        else {
            super.service(request, response);
        }
    }

    @Override
    protected final void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        // 主要通过此方法实现请求的分发
        processRequest(request, response);
    }
image.gif

有源码可知,在doGet 方法中有调用了 processRequest方法,其他doXX中同样调用了此方法,接下来看看 此方法。

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

        long startTime = System.currentTimeMillis();
        Throwable failureCause = null;
         // 获取LocaleContextHolder 中保存的LocalContext
        LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();

        //获取当前请求的LocaleContext
        LocaleContext localeContext = buildLocaleContext(request);

        //获取RequestContextHolder中保存的RequestAttributes
        RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
        //获取当前请求的ServletRequestAttributes
        ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

        // 略 .....

        //将当前请求的LocaleContext和ServletRequestAttributes保存到
        //LocaleConextHolder和RequestContextHolder中
        initContextHolders(request, localeContext, requestAttributes);

        try {
            //模板方法,在子类中实现
            doService(request, response);
        }catch (ServletException | IOException ex) {
            //略 ....
        }catch (Throwable ex) {
            //略 ....
        }finally {
             //恢复之前的LocaleContext和ServletRequestAttributes到
             //LocaleConextHolder和RequestContextHolder中
            resetContextHolders(request, previousLocaleContext, previousAttributes);
            if (requestAttributes != null) {
                requestAttributes.requestCompleted();
            }

            //发布ServletRequestHandledEvent消息
            publishRequestHandledEvent(request, response, startTime, failureCause);
        }
    }
image.gif

说明: 由于在请求时把LocaleContext和ServletRequestAttributes保存到了LocaleContextHolder和RequestContextHolder中,这两个类都是在ThreadLocal中所有在后续的方法中可以根据通过这两个类获取LocalContext和ServletRequestAttributes,从而获取HttpServletRequest、HttpServletResponse和HttpSession。

4.3、DispatcherServlet中实现了 doService方法,

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {

         //省略 部分代码 .....

        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);
                }
            }
        }
    }
image.gif

有源码可知 doService方法中主要是调用了 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 {
            ModelAndView mv = null;
            Exception dispatchException = null;

            try {
                //检测是否为上传请求
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);

                // Determine handler for the current request.
                //根据 request 查找对应的 Handler 
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // Determine handler adapter for the current request.
                //根据 Handler查找执行此Handler的 handler适配器
                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 (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }

                //执行拦截器的前置方法
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                // Actually invoke the handler.
                //实际调用 Handler
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

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

                applyDefaultViewName(processedRequest, mv);
                //执行拦截器中的 postHandle 方法
                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);
            }
            //处理返回结果。包括处理异常、渲染页面、发出完成通知调用拦截器的 afterComletion方法
            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);
                }
            }
        }
    }
image.gif

五、总结

本篇文章主要是分析了DispatcherServlet 的初始化和请求的响应过程,接下来将会分析 Handler的查找过程。

上一篇下一篇

猜你喜欢

热点阅读