关于spring-mvc:Spring-MVC-源码之请求的执行流程

46次阅读

共计 15209 个字符,预计需要花费 39 分钟才能阅读完成。

Spring MVC 源码之申请的执行流程

DispatcherServlet

SpringMVC 外围就是 DispatcherServlet,所有得申请都会转发到 DispatcherServlet, 而后再通过 DispatcherServlet 执行具体得管制层(Handler)返回 ModelAndView 给客户端视图展现。

DispatcherServlet 其实就是一个 Servlet 类,无非就是包装一层,通过 url 可能映射找到咱们得 SpringMvc 中定义得申请办法。

  1. 类的集成关系

    DispatcherServlet 继承 FrameworkServlet 继承 HttpServlet

    面向基本上思维 重写 先走父类,在走子类。

    得出答案:先看 HttpServlet 再找到咱们最初的子类

    当咱们第一次申请一个地址的时候,如果可能拜访,他会返回一个 200 代表拜访胜利,此时应答头信息中会有一个 Last-Modified 代表服务器这个文件的最初批改工夫。

    当咱们再次申请的时候,如果申请过了,就会在申请头,有一个 If-Modified-Since 的值,这时候传到服务器,服务器就会拿这个值和上次批改的工夫比照,如果小于上次批改的工夫,阐明服务器上的文件被批改过,就再次从服务器进行下载,返回 200

    如果没有批改就像上图一样,返回一个 304,代表客户端曾经执行了 GET,但文件未变动。

既然是 Servlet 类,那么他有一个最终的办法,就是 service()办法,他是 Servlet 最外围的办法。

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 = req.getDateHeader(HEADER_IFMODSINCE);
                if (ifModifiedSince < lastModified) {// 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);
        }
    }

maybeSetLastModified(resp, lastModified);

    /*
     * Sets the Last-Modified entity header field, if it has not
     * already been set and if the value is meaningful.  Called before
     * doGet, to ensure that headers are set before response data is
     * written.  A subclass might have set this header already, so we
     * check.
     */
    private void maybeSetLastModified(HttpServletResponse resp,
                                      long lastModified) {if (resp.containsHeader(HEADER_LASTMOD))
            return;
        if (lastModified >= 0)
            resp.setDateHeader(HEADER_LASTMOD, lastModified);
    }

因而,咱们在 HttpServletBean 类中找 service 办法,发现没有,咱们持续往上一层 FrameworkServlet 类中找,发现找到了,因而 spring 实现该办法在这个类去实现的。

    /**
     * Override the parent class implementation in order to intercept PATCH requests.
     */
    @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);
        }
  }

这里职责次要是先拿到一个申请,而后又做了一个判断申请形式。发现不是 PATCH 形式就去调用父类(HttpServlet)中 service()办法。他去调用用父类中的 service 办法其实就是去调用该类中 doPost(),doGet()办法,拿到不同的申请形式而后解决不同的业务。比方以 FrameworkServlet 的 get 形式为例:

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

FrameWorkServlet#processRequest

这个办法外面能够间接看到 this.doService(request, response); 办法。在 debug 期间,进去该办法,发现这个办法间接跳到DispatcherServlet 类中,由上可知,这个办法就像始终被子类重写。

    /**
     * Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
     * for the actual dispatching.
     */
    @Override
    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);
                }
            }
        }
  }

进入 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.
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null) {noHandlerFound(processedRequest, response);
                    return;
                }

                // Determine handler adapter for the current request.
                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.
                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);
                }
            }
        }
    }

首先次要是创立一个视图对象 ModelAndView mv = null; 而后查看以后申请是否是二进制的申请 processedRequest = this.checkMultipart(request); 而后就是只有代码

mappedHandler = this.getHandler(processedRequest);

就是依据以后的申请去拿一个 Handler.(在这个源码中 springMVC 都是应用的 Handler, 那么他到时是什么?这个 Handler 其实就是咱们的控制器,包含咱们写 Controller)。进入这个办法源码如下:

    /**
     * Return the HandlerExecutionChain for this request.
     * <p>Tries all handler mappings in order.
     * @param request current HTTP request
     * @return the HandlerExecutionChain, or {@code null} if no handler could be found
     */
    @Nullable
    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {if (this.handlerMappings != null) {for (HandlerMapping mapping : this.handlerMappings) {HandlerExecutionChain handler = mapping.getHandler(request);
                if (handler != null) {return handler;}
            }
        }
        return null;
    }

由流程图可知,发送清求到控制器,控制器第二个节点就是发送第二个申请就是去拿 Handler,因而可知这里才是最外围代码。由图可知他取 Handler 最终要去找 HandlerMapping, 而后他再去拿一个 Handler。那么为什么要去找 HandlerMapping 去要一个 Handler 呢?

首先咱们在配置控制器的时候有两种形式 1.xml 形式,2. 注解的形式。因而 spring 源码他给咱们不止一种控制器。因为两种形式控制器。因而 spring 并不知道咱们应用的是哪一种控制器。因为两种控制器,spring 去底层去找的管制的实现形式是不一样的。因而这就是为什么第二步他要去找 Handler(控制器)的了。然而 Handler 怎么找的到呢? 就是通过 HandlerMapping 这样一个处理器映射器。

Handler 分装了咱们创立的 Controller 和一个拦截器。

因而到这里咱们就拿到了对应的也是最合适的 Handler, 而后返回中央处理器。

第二个办法:

HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());

获取控制器的适配器。也就是咱们之前拿到了控制器,接下来要去执行控制器,也就是拿到控制器适配器中执行控制器。这里为什么要获取适配器呢?因为跟控制器映射器(也就是配置形式)一样。

接下来判断你有没有须要执行的拦截器。:

if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}
    /**
     * Apply preHandle methods of registered interceptors.
     * @return {@code true} if the execution chain should proceed with the
     * next interceptor or the handler itself. Else, DispatcherServlet assumes
     * that this interceptor has already dealt with the response itself.
     */
    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {HandlerInterceptor[] interceptors = getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {for (int i = 0; i < interceptors.length; i++) {HandlerInterceptor interceptor = interceptors[i];
                if (!interceptor.preHandle(request, response, this.handler)) {triggerAfterCompletion(request, response, null);
                    return false;
                }
                this.interceptorIndex = i;
            }
        }
        return true;
    }

适配器去执行 Handler

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

如果你有 ModelAndView,就返回一个 ModelAndView. 而后返回给试图对象,而后把视图对象交给视图解析器,去渲染,最初响应给用户。

因而总结,spring 提供了两种 HandlerMapping 以及三种 HandlerAdapter. 他们运行匹配的关系如图:

那么运行时他怎么能找到这些呢?spring 是怎么配置提供的呢?

其实他们在 spring 配置文件就曾经配置好了,当 springMVC 初始化时就加载实例化,获取这些对象。他们是被配置在 spring 的 SpringwebMVC 架包的 servlet 架包中的 DispatcherServlet.properties 配置文件中

DispatcherServlet 源码流程剖析

1. 执行 doDispatch

2. 调用 getHandler 办法获取申请指标的办法 也就是 申请 url 映射门路对应的管制层具体的办法

handlerMappings 的作用查找控制器地位,比方 xml 和注解形式。

3. 调用 getHandlerAdapter 获取管制层适配器 RequestMappingHandlerAdapter

4. 执行拦截器前置办法 preHandle() 如果返回为 true 的话

5. 执行理论申请指标办法 返回 modeAndView 对象

6. 执行拦截器 PostHandle()办法

7. 设置渲染视图层内容

8. 执行拦截器 afterCompletion 办法

HandlerMappings

DispatcherServlet#initHandlerMappings

/**
 * Initialize the HandlerMappings used by this class.
 * <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace,
 * we default to BeanNameUrlHandlerMapping.
 */
private void initHandlerMappings(ApplicationContext context) {
   this.handlerMappings = null;

   if (this.detectAllHandlerMappings) {
      // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
      Map<String, HandlerMapping> matchingBeans =
            BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
      if (!matchingBeans.isEmpty()) {this.handlerMappings = new ArrayList<>(matchingBeans.values());
         // We keep HandlerMappings in sorted order.
         AnnotationAwareOrderComparator.sort(this.handlerMappings);
      }
   }
   else {
      try {HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
         this.handlerMappings = Collections.singletonList(hm);
      }
      catch (NoSuchBeanDefinitionException ex) {// Ignore, we'll add a default HandlerMapping later.}
   }

   // Ensure we have at least one HandlerMapping, by registering
   // a default HandlerMapping if no other mappings are found.
   if (this.handlerMappings == null) {this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
      if (logger.isTraceEnabled()) {logger.trace("No HandlerMappings declared for servlet'" + getServletName() +
               "': using default strategies from DispatcherServlet.properties");
      }
   }
}

servlet 初始化会调用 init 办法,换句话说就是 springMVC 进行初始化的时候首先会去执行 HttpServletBean 的 init 办法,上面看看 HttpServletBean 的源码:

    /**
     * Map config parameters onto bean properties of this servlet, and
     * invoke subclass initialization.
     * @throws ServletException if bean properties are invalid (or required
     * properties are missing), or if subclass initialization fails.
     */
    @Override
    public final void init() throws ServletException {

        // Set bean properties from init parameters.
        PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
        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;
            }
        }

        // Let subclasses do whatever initialization they like.
        initServletBean();}

获取你在 web.xml 中配置在 <init-param> 中的属性(例如:namespace,contextConfigLocation)。其中有一点值得注意,那就是 initServletBean() 这个办法是由其子类 FrameworkServlet 实现,因而,接下来 FramworkServlet 会执行 initServletBean 这个办法,上面就持续看看 initServletBean 办法源码:
FrameWorkservlet#initServletBean

    @Override
    protected final void initServletBean() throws ServletException {getServletContext().log("Initializing Spring" + getClass().getSimpleName() + "'" + getServletName() + "'");
        if (logger.isInfoEnabled()) {logger.info("Initializing Servlet'" + getServletName() + "'");
        }
        long startTime = System.currentTimeMillis();

        try {this.webApplicationContext = initWebApplicationContext();
            initFrameworkServlet();}
        catch (ServletException | RuntimeException ex) {logger.error("Context initialization failed", ex);
            throw ex;
        }

        if (logger.isDebugEnabled()) {
            String value = this.enableLoggingRequestDetails ?
                    "shown which may lead to unsafe logging of potentially sensitive data" :
                    "masked to prevent unsafe logging of potentially sensitive data";
            logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
                    "': request parameters and headers will be" + value);
        }

        if (logger.isInfoEnabled()) {logger.info("Completed initialization in" + (System.currentTimeMillis() - startTime) + "ms");
        }
    }

initServletBean 办法中就调用了一个 initFrameworkServlet 办法和 initWebApplicationContext 办法,其中 initFrameworkServlet 办法是由子类实现,这个不多说,间接看 initWebApplicationContext 办法源码:

protected WebApplicationContext initWebApplicationContext() {
 
        // 此处的 rootContext 在你配置了 ContextLoaderListener 的时候注入的
        // 通过剖析 ContextLoaderListenr 的源码,能够看到
        //ContextLoaderListener 通过 ContextLoader 依据 ApplicationContext.xml 的配置会创立一个 xmlWebApplicationContext
        // 如果没有配置 ContextLoaderListener, 本处将为 null, 但不影响 springMVC, 为何?通过接下来的剖析,就能看到起因
        WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
        WebApplicationContext wac = null;
       
       // 当 webApplicationContext 曾经存在,那么就间接应用,应用之前会先设置 rootContext, 为其跟。// 配置实现之后 refresh 一次,refresh 会波及到 IOC 的内容,本处不做探讨。if (this.webApplicationContext != null) {
            wac = this.webApplicationContext;
            if (wac instanceof ConfigurableWebApplicationContext) {ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)wac;
                if (!cwac.isActive()) {if (cwac.getParent() == null) {cwac.setParent(rootContext);
                    }
 
                    this.configureAndRefreshWebApplicationContext(cwac);
                }
            }
        }
        // 如果不存在 webApplicationContext, 那么先去 ServletContext 中查找
        if (wac == null) {wac = this.findWebApplicationContext();
        }
        // 如果上述没有查到,那么就创立 webApplicationContext
        if (wac == null) {wac = this.createWebApplicationContext(rootContext);
        }
 
        if (!this.refreshEventReceived) {
             // 此办法由 DispatcherServlet 调用
            this.onRefresh(wac);
        }
        // 将 webApplicationContext 保留在 ServletContext
        if (this.publishContext) {
            // 将上下文公布为 servlet 上下文属性。String attrName = this.getServletContextAttributeName();
            this.getServletContext().setAttribute(attrName, wac);
            if (this.logger.isDebugEnabled()) {this.logger.debug("Published WebApplicationContext of servlet'" + this.getServletName() + "'as ServletContext attribute with name [" + attrName + "]");
            }
        }
 
        return wac;
    }

protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
        
       // 此处的 contextClass 可在 web.xml 中的《init-param》中指定
       // 如果没有配置,那么默认的是 XmlWebApplicationContext.class
        Class<?> contextClass = this.getContextClass();
        if (this.logger.isDebugEnabled()) {this.logger.debug("Servlet with name'" + this.getServletName() + "'will try to create custom WebApplicationContext context of class'" + contextClass.getName() + "', using parent context [" + parent + "]");
        }
 
        if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {throw new ApplicationContextException("Fatal initialization error in servlet with name'" + this.getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext");
        } else {
             // 此处利用反射创立
            ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
            wac.setEnvironment(this.getEnvironment());
            wac.setParent(parent);
            String configLocation = this.getContextConfigLocation();
            if (configLocation != null) {wac.setConfigLocation(configLocation);
            }
            //refresh 一次,这里不多说
            this.configureAndRefreshWebApplicationContext(wac);
            return wac;
        }

SpringMVC 管制层容器初始化

  1. HttpServletBean init ()办法
  2. FrameworkServlet initServletBean 办法→ initWebApplicationContext();
  3. DispatcherServlet onRefresh 办法→ initStrategies()办法

正文完
 0