1. DispatcherServlet初始化流程

我们已知道Spring的模型-视图-控制器(MVC)框架是围绕一个DispatcherServlet来设计的,这个Servlet会把请求分发给各个处理器,并支持可配置的处理器映射、视图渲染、本地化、时区与主题渲染等,甚至还能支持文件上传。下面是DispatcherServlet的类结构图:
DispatcherServlet

DispatcherServlet是一个Servlet且继承了HttpServlet,它必然有Servlet的生命周期“init-service-destroy”,下面我们便从init来展开分析。

1.1 HttpServletBean#init()源码解析

DispatcherServlet的init()方法是在HttpServletBean中实现的,HttpServletBean重写了GenericServlet的init()方法,主要作用是加载 web.xml 中 DispatcherServlet 的 <init-param> 配置,并调用子类的初始化,源码如下:

@Override
public final void init() throws ServletException {
    //ServletConfigPropertyValues是静态内部类,它可以通过requiredProperties参数校验哪些属性是必要的,将ServletConfig中的初始化参数都包装成PropertyValue对象放入List集合中
    PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
    //使用 BeanWrapper 来构造 DispatcherServlet,设置DispatcherServlet属性
    if (!pvs.isEmpty()) {
        try {
            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;
        }
    }

    // 抽象方法,目的是调用子类的初始化方法
    initServletBean();

    if (logger.isDebugEnabled()) {
        logger.debug("Servlet '" + getServletName() + "' configured successfully");
    }
}
/**
 * config表示web.xml的属性配置
 * requiredProperties表示的是必须有的属性
 */
public ServletConfigPropertyValues(ServletConfig config, Set<String> requiredProperties)
                throws ServletException {
    //如果requiredProperties不为空则重新创建一个Set集合
    Set<String> missingProps = (!CollectionUtils.isEmpty(requiredProperties) ?
            new HashSet<String>(requiredProperties) : null);
    //获取web.xml中的所有初始化参数的参数名枚举
    Enumeration<String> paramNames = config.getInitParameterNames();
    //将所有的初始化参数包装成PropertyValue对象并放入List集合中
    while (paramNames.hasMoreElements()) {
        String property = paramNames.nextElement();
        Object value = config.getInitParameter(property);
        addPropertyValue(new PropertyValue(property, value));
        if (missingProps != null) {
            missingProps.remove(property);
        }
    }

    //如果缺少了必须的属性则抛出ServletException异常
    if (!CollectionUtils.isEmpty(missingProps)) {
        throw new ServletException(
                "Initialization from ServletConfig for servlet '" + config.getServletName() +
                "' failed; the following required properties were missing: " +
                StringUtils.collectionToDelimitedString(missingProps, ", "));
    }
}

1.2 FramworkServelt#initServletBean源码解析

initServletBean()方法是在FramworkServelt中实现,用于初始化WebApplicationContext容器

@Override
protected final void initServletBean() throws ServletException {
    getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
    if (this.logger.isInfoEnabled()) {
        this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
    }
    long startTime = System.currentTimeMillis();

    try {
        //初始化WebApplicationContext容器
        this.webApplicationContext = initWebApplicationContext();
        //暂时没有具体的实现,可以留给开发人员在子类中实现一些特有的属性
        initFrameworkServlet();
    }
    catch (ServletException ex) {
        this.logger.error("Context initialization failed", ex);
        throw ex;
    }
    catch (RuntimeException ex) {
        this.logger.error("Context initialization failed", ex);
        throw ex;
    }

    if (this.logger.isInfoEnabled()) {
        long elapsedTime = System.currentTimeMillis() - startTime;
        this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
                elapsedTime + " ms");
    }
}

/**
 * Initialize and publish the WebApplicationContext for this servlet.
 * <p>Delegates to {@link #createWebApplicationContext} for actual creation
 * of the context. Can be overridden in subclasses.
 */
protected WebApplicationContext initWebApplicationContext() {
    //从ServletContext中获取根的Context对象,如使用Spring则是Spring的IOC容器
    WebApplicationContext rootContext =
            WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    WebApplicationContext wac = null;
    // 如果当前的WebApplicationContext在构造时已经被注入则使用它
    if (this.webApplicationContext != null) {
        wac = this.webApplicationContext;
        //如果context对象是ConfigurableWebApplicationContext
        if (wac instanceof ConfigurableWebApplicationContext) {
            ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
             //如果ConfigurableWebApplicationContext 不是存活状态
            if (!cwac.isActive()) {
                // The context has not yet been refreshed -> provide services such as
                // setting the parent context, setting the application context id, etc
                if (cwac.getParent() == null) {
                    // The context instance was injected without an explicit parent -> set
                    // the root application context (if any; may be null) as the parent
                    cwac.setParent(rootContext);
                }
                //配置并刷新WebApplicationContext
                configureAndRefreshWebApplicationContext(cwac);
            }
        }
    }
    if (wac == null) {
        // 如果在该实例构造时context没有被注入,则查询是否已经注册在ServletContext中
        wac = findWebApplicationContext();
    }
    if (wac == null) {
        // 在ServletContext没有context实例被定义则通过rootContext创建一个
        wac = createWebApplicationContext(rootContext);
    }
    //refreshEventReceived默认为false
    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.
        onRefresh(wac);
    }
    // publishContext默认为true,允许把当前容器作为ServeltContext的一个属性
    if (this.publishContext) {
        String attrName = getServletContextAttributeName();
        getServletContext().setAttribute(attrName, wac);
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
                    "' as ServletContext attribute with name [" + attrName + "]");
        }
    }
    return wac;
}


protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
    //判断应用上下文唯一的id,Class+HashCode
    if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
        //基于可用的信息配置一个更有用的id
        if (this.contextId != null) {
            wac.setId(this.contextId);
        }
        else {
            //设置默认的id值,如“org.springframework.web.context.WebApplicationContext:/dispatcher”
            wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                    ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
        }
    }

    wac.setServletContext(getServletContext());
    wac.setServletConfig(getServletConfig());
    wac.setNamespace(getNamespace());
    //针对ContextRefreshListener事件监听
    wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

    // The wac environment's #initPropertySources will be called in any case when the context
    // is refreshed; do it eagerly here to ensure servlet property sources are in place for
    // use in any post-processing or initialization that occurs below prior to #refresh
    ConfigurableEnvironment env = wac.getEnvironment();
    //当上下文被刷新时调用上下文环境的initPropertySources方法
    if (env instanceof ConfigurableWebEnvironment) {
        ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
    }
    //在刷新和激活作为这个servlet的上下文之前,对给定的WebApplicationContext进行后置处理,这个方法默认实现为空,可以由开发人员自定义
    postProcessWebApplicationContext(wac);
    // spring dispatcherServlet初始化的时候可以指定初始化一些类
    applyInitializers(wac);
    wac.refresh();
}


protected WebApplicationContext findWebApplicationContext() {
    //获取在ServletContext中的属性名
    String attrName = getContextAttribute();
    if (attrName == null) {
        return null;
    }
    //通过属性名查找ServletContext中的WebApplicationContext
    WebApplicationContext wac =
            WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName);
    if (wac == null) {
        throw new IllegalStateException("No WebApplicationContext found: initializer not registered?");
    }
    return wac;
}

protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
    //获取contextClass,默认值是XmlWebApplicationContext.class
    Class<?> contextClass = getContextClass();
    if (this.logger.isDebugEnabled()) {
        this.logger.debug("Servlet with name '" + getServletName() +
                "' will try to create custom WebApplicationContext context of class '" +
                contextClass.getName() + "'" + ", using parent context [" + parent + "]");
    }
    //判断ConfigurableWebApplicationContext是否与contextClass相同或是它的父类或接口
    if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
        throw new ApplicationContextException(
                "Fatal initialization error in servlet with name '" + getServletName() +
                "': custom WebApplicationContext class [" + contextClass.getName() +
                "] is not of type ConfigurableWebApplicationContext");
    }
    //根据Class类型创建实例
    ConfigurableWebApplicationContext wac =
            (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

    wac.setEnvironment(getEnvironment());
    wac.setParent(parent);
    wac.setConfigLocation(getContextConfigLocation());
    //配置并刷新WebApplicationContext
    configureAndRefreshWebApplicationContext(wac);

    return wac;
}

2. DispatcherServlet处理流程

2.1 源码分析

我们知道请求一个Servlet会调用其service()方法,查看源码发现FrameworkServlet重写了HttpServlet的service()方法,源码如下:

@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    //获取http请求方法
    HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
    //如果Http请求方法是PATCH类型或为空
    if (HttpMethod.PATCH == httpMethod || httpMethod == null) {
        processRequest(request, response);
    }
    else {
        //调用HttpServlet的service方法
        super.service(request, response);
    }
}
/**
 * HttpServlet#service方法
 * @desc 由于HttpServlet中doHead方法中调用doGet来处理请求,因此FrameworkServlet重写了处理
 * GET, POST, PUT, DELETE, OPTIONS, TRACE请求的方法,这些方法都做相关处理后委托给processRequest方法来处理
 */
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      String method = req.getMethod();
      long lastModified;
      if (method.equals("GET")) {
          lastModified = this.getLastModified(req);
          if (lastModified == -1L) {
              this.doGet(req, resp);
          } else {
              long ifModifiedSince = req.getDateHeader("If-Modified-Since");
              if (ifModifiedSince < lastModified / 1000L * 1000L) {
                  this.maybeSetLastModified(resp, lastModified);
                  this.doGet(req, resp);
              } else {
                  resp.setStatus(304);
              }
          }
      } else if (method.equals("HEAD")) {
          lastModified = this.getLastModified(req);
          this.maybeSetLastModified(resp, lastModified);
          this.doHead(req, resp);
      } else if (method.equals("POST")) {
          this.doPost(req, resp);
      } else if (method.equals("PUT")) {
          this.doPut(req, resp);
      } else if (method.equals("DELETE")) {
          this.doDelete(req, resp);
      } else if (method.equals("OPTIONS")) {
          this.doOptions(req, resp);
      } else if (method.equals("TRACE")) {
          this.doTrace(req, resp);
      } else {
          String errMsg = lStrings.getString("http.method_not_implemented");
          Object[] errArgs = new Object[]{method};
          errMsg = MessageFormat.format(errMsg, errArgs);
          resp.sendError(501, errMsg);
      }
 }

/**
 * 集中处理Http各种类型的请求
 */
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

    long startTime = System.currentTimeMillis();
    Throwable failureCause = null;
    //封装当前请求的Local信息
    LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
    LocaleContext localeContext = buildLocaleContext(request);
    //封装当前请求的request,response信息,方法操作
    RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
    ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
    //异步请求的相关管理
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
    //将LocaleContext、RequestAttributes 分别封装进 LocaleContextHolder、RequestContextHolder 
    //LocaleContextHolder、RequestContextHolder 这两个抽象类其实都是利用 ThreadLocal 将信息存放在线程中
    initContextHolders(request, localeContext, requestAttributes);

    try {
        //DispatcherServlet中实现
        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 {
        //释放LocaleContext、RequestAttributes
        resetContextHolders(request, previousLocaleContext, previousAttributes);
        if (requestAttributes != null) {
            requestAttributes.requestCompleted();
        }

        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");
                }
            }
        }
        //发布请求处理完毕事件,包括获取状态码和发布事件
        publishRequestHandledEvent(request, response, startTime, failureCause);
    }
}

DispatcherServlet的doService()方法设置各种request属性后调用doDispatch()方法利用各种处理器来处理请求,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);

            // 通过HandlerMapping获取HandlerExecutionChain
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null || mappedHandler.getHandler() == null) {
                noHandlerFound(processedRequest, response);
                return;
            }

            // 通过handler获取HandlerAdapter
            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;
                }
            }
            // HandlerExecutionChain调用所有拦截器的preHandle方法
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }

            // 通过HandlerAdapter获取ModelAndView
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

            if (asyncManager.isConcurrentHandlingStarted()) {
                return;
            }
            // 如果ModelAndView非空且view为空使用默认ViewName
            applyDefaultViewName(processedRequest, mv);
            // HandlerExecutionChain调用所有拦截器的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);
        }
        // 处理ModelAndView和Exception
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    catch (Exception ex) {
        //调用所有拦截器的afterCompletion方法
        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);
            }
        }
    }
}
/**
 * 处理ModelAndView
 */
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
            HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {

    boolean errorView = false;
    //异常处理部分,获取error视图
    if (exception != null) {
        if (exception instanceof ModelAndViewDefiningException) {
            logger.debug("ModelAndViewDefiningException encountered", exception);
            mv = ((ModelAndViewDefiningException) exception).getModelAndView();
        } else {
            Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
            mv = processHandlerException(request, response, handler, exception);
            errorView = (mv != null);
        }
    }

    // 处理程序是否返回一个视图来渲染?
    if (mv != null && !mv.wasCleared()) {
        // 获取视图和渲染视图
        render(mv, request, response);
        // 视图是error视图,则移除Request中Error属性
        if (errorView) {
            WebUtils.clearErrorRequestAttributes(request);
        }
    } else {
        if (logger.isDebugEnabled()) {
            logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
                    "': assuming HandlerAdapter completed request handling");
        }
    }

    if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
        // Concurrent handling started during a forward
        return;
    }
    // 依次调用所有拦截器的 afterCompletion方法
    if (mappedHandler != null) {
        mappedHandler.triggerAfterCompletion(request, response, null);
    }
}
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
    // 确定请求的区域设置并将其应用于响应
    Locale locale = this.localeResolver.resolveLocale(request);
    response.setLocale(locale);

    View view;
    // 判断ModelAndView的view属性是否是String类型,如果是String类型使用视图解析器解析
    if (mv.isReference()) {
        // 解析视图名
        view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
        if (view == null) {
            throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
                    "' in servlet with name '" + getServletName() + "'");
        }
    }
    else {
        // No need to lookup: the ModelAndView object contains the actual View object.
        view = mv.getView();
        if (view == null) {
            throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
                    "View object in servlet with name '" + getServletName() + "'");
        }
    }

    // Delegate to the View object for rendering.
    if (logger.isDebugEnabled()) {
        logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
    }
    try {
        // 设置响应状态
        if (mv.getStatus() != null) {
            response.setStatus(mv.getStatus().value());
        }
        // View对象根据ModelAndView渲染视图
        view.render(mv.getModelInternal(), request, response);
    }
    catch (Exception ex) {
        if (logger.isDebugEnabled()) {
            logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
                    getServletName() + "'", ex);
        }
        throw ex;
    }
}

/**
 * 解析视图名称
 */
protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,
            HttpServletRequest request) throws Exception {
    // 遍历视图解析器并解析视图
    for (ViewResolver viewResolver : this.viewResolvers) {
        View view = viewResolver.resolveViewName(viewName, locale);
        if (view != null) {
            return view;
        }
    }
    return null;
}

关于HanlderMapping和HandlerAdapter的详解见SpringMVC源码解析之HandlerMapping与HandlerAdapter,关于视图解析方面的详解见SpringMVC源码解析之ViewResolver。

2.2 流程总结

请求执行流程图如下:
DipatcherServlet
总结SpringMVC的主要执行流程如下:

  1. Tomcat 启动,对 DispatcherServlet 进行实例化,然后调用它的 init() 方法进行初始化,在这个初始化过程中完成了:对 web.xml 中初始化参数的加载;建立 WebApplicationContext (SpringMVC的IOC容器);进行组件的初始化;
  2. 客户端发出请求,由 Tomcat 接收到这个请求,如果匹配 DispatcherServlet 在 web.xml 中配置的映射路径,Tomcat 就将请求转交给 DispatcherServlet 处理;
  3. DispatcherServlet 从容器中取出所有 HandlerMapping 实例(每个实例对应一个 HandlerMapping 接口的实现类)并遍历,每个 HandlerMapping 会根据请求信息,通过自己实现类中的方式去找到处理该请求的 Handler (执行程序,如Controller中的方法),并且将这个 Handler 与一堆 HandlerInterceptor (拦截器) 封装成一个 HandlerExecutionChain 对象,一旦有一个 HandlerMapping 可以找到 Handler 则退出循环;
  4. DispatcherServlet 取出 HandlerAdapter 组件,根据已经找到的 Handler,再从所有 HandlerAdapter 中找到可以处理该 Handler 的 HandlerAdapter 对象;
  5. 执行 HandlerExecutionChain 中所有拦截器的 preHandler() 方法,然后再利用 HandlerAdapter 执行 Handler ,执行完成得到 ModelAndView,再依次调用拦截器的 postHandler() 方法;
  6. 利用 ViewResolver 将 ModelAndView 或是 Exception(可解析成 ModelAndView)解析成 View,然后 View 会调用 render() 方法再根据 ModelAndView 中的数据渲染出页面;
  7. 最后再依次调用拦截器的 afterCompletion() 方法,这一次请求就结束了。
    ………
Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐