Spring MVC 源碼之請求的執行流程

Spring MVC 源碼之請求的執行流程

image

DispatcherServlet

SpringMVC核心就是DispatcherServlet,全部得請求都會轉發到DispatcherServlet,而後再經過DispatcherServlet執行具體得控制層(Handler)返回ModelAndView給客戶端視圖展現。java

image

DispatcherServlet其實就是一個Servlet類,無非就是包裝一層,經過url可以映射找到咱們得SpringMvc中定義得請求方法。ios

  1. 類的集成關係

    DispatcherServlet繼承FrameworkServlet繼承HttpServletweb

    面向基本上思想 重寫 先走父類 ,在走子類。spring

    得出答案:先看HttpServlet再找到咱們最後的子類服務器

    當咱們第一次請求一個地址的時候,若是可以訪問,他會返回一個200表明訪問成功,此時應答頭信息中會有一個 Last-Modified 表明服務器這個文件的最後修改時間。app

    當咱們再次請求的時候,若是請求過了,就會在請求頭,有一個If-Modified-Since的值,這時候傳到服務器,服務器就會拿這個值和上次修改的時間對比,若是小於上次修改的時間,說明服務器上的文件被修改過,就再次從服務器進行下載,返回200less

    若是沒有修改就像上圖同樣,返回一個304,表明客戶端已經執行了GET,但文件未變化。async

image

既然是Servlet類,那麼他有一個最終的方法,就是service()方法,他是Servlet最核心的方法。ide

HttpServlet#servicepost

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

image

這個方法裏面能夠直接看到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這樣一個處理器映射器。
image

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.他們運行匹配的關係如圖:

image

那麼運行時他怎麼能找到這些呢?spring是怎麼配置提供的呢?

其實他們在spring配置文件就已經配置好了,當springMVC初始化時就加載實例化,獲取這些對象。他們是被配置在spring的SpringwebMVC架包的servlet架包中的DispatcherServlet.properties配置文件中
image

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()方法
相關文章
相關標籤/搜索