springBoot2--11-springMVC流程简单分析

springMVC流程简单分析

本文是基于springboot对MVC的流程的一些解析,如有错误,还望指正。

下图是找的一张SpringMvc的执行流程图,后文都是按图中的顺序进行讲解。

话不多说,开始
在这里插入图片描述

  • 1、用户请求
  • 2、通过调度器将请求发送给处理器映射器
  • 3、在处理器映射器中找到对应的处理器
  • 4、将处理器与已注册的拦截器,封装为HandlerExecutionChain,并返回给调度器进行具体打操作
  • 5----8、找到能够使用的处理器适配器来处理该handler
  • 9、然后就是视图解析,解析成功后返回给用户

1、请求

当我们输入请求后,程序就会调用doService方法

//该方法来自:DispatcherServlet.class
/**
 * Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
 * for the actual dispatching.
 * 翻译:将DispatcherServlet特定的请求属性和委托公开给{@link#doDispatch}进行实际调度。
 */
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
   //与日志相关,咱就不讨论了
    logRequest(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;
    //检查确定给定的请求是否是包含请求,即不是来自外部的顶级HTTP请求。
    //检查是否存在“javax.servlet.include.request\u uri”请求属性。
   if (WebUtils.isIncludeRequest(request)) {
      attributesSnapshot = new HashMap<>();
       //获取所有的属性名
      Enumeration<?> attrNames = request.getAttributeNames();
      while (attrNames.hasMoreElements()) {
         String attrName = (String) attrNames.nextElement();
   /**
     * this.cleanupAfterInclude :是否在包含请求后执行请求属性的清理?
     * attrName.startsWith(DEFAULT_STRATEGIES_PREFIX):检查获取的属性名的前缀是不是:							 *          org.springframework.web.servlet
     * attributesSnapshot.put(attrName, request.getAttribute(attrName)):
     *          保留请求属性的快照
     */
         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());
   /**
     * 做过Web开发的人都知道,后端有请求转发和请求重定向两种方式,
     * 请求转发的时候Request是同一个,所以可以在转发后拿到转发前的所有信息;
     * 但是重定向后 Request是新的,如果需要在重定向前设置一些信息,重定向后获取使用应该怎么办法呢?
     * 这就是 FlashMap存在的意义,FlashMap 借助 session 重定向前通过 FlashMapManager将信息放入FlashMap,
     * 重定向后 再借助 FlashMapManager 从 session中找到重定向后需要的 FalshMap
     *	如果想要深入的了解,可以搜搜:FlashMap,FlashMapManager
     */
   if (this.flashMapManager != null) {
      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);
   }

   RequestPath previousRequestPath = null;
   if (this.parseRequestPath) {
      previousRequestPath = (RequestPath) request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE);
      ServletRequestPathUtils.parseAndCache(request);
   }

   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);
         }
      }
      if (this.parseRequestPath) {
         ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request);
      }
   }
}

2、调度器(DispatchServlet)

//该方法来自:DispatcherServlet.class  
/**
     * 处理对处理程序的实际分派。
     * 处理程序将通过按顺序应用servlet的HandlerMappings获得。
     * HandlerAdapter将通过查询servlet安装的HandlerAdapter来获得,
     * 以找到第一个支持handler类的HandlerAdapter。
     *所有HTTP方法都由此方法处理。由HandlerAdapter或处理程序自己决定哪些方法是可接受的。
     *
     */
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
   HttpServletRequest processedRequest = request;
   //处理程序执行链
    /**
     * 当用户请求到到DispaterServlet中后,
     * 配置的HandlerMapping会根据用户请求(也就是handler)
     * 会将它(也就是handler)与所有的interceptors封装为HandlerExecutionChain对象
     *
     */
   HandlerExecutionChain mappedHandler = null;
    //文件上传解析
   boolean multipartRequestParsed = false;

   WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

   try {
      ModelAndView mv = null;
      Exception dispatchException = null;

      try {
          //检查是否为复合请求(文件上传),如果是,将其解析,并封装好后返回。
         processedRequest = checkMultipart(request);
          //这个判断的意义在于:如果这是个复合请求,那么它和原请求就不一样,就会为false,反之则不是。
         multipartRequestParsed = (processedRequest != request);

         // Determine handler for the current request.
          //1、确定当前请求的处理程序。下面会重点谈谈
         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.
          //处理上次修改的标头(如果处理程序支持)。例如:GET、POST...
         String method = request.getMethod();
          //判断是否为GET请求
         boolean isGet = HttpMethod.GET.matches(method);
         if (isGet || HttpMethod.HEAD.matches(method)) {
             //获取最后修改时间
            long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
            if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
               return;
            }
         }

          //调用已注册拦截器的预处理方法。
         if (!mappedHandler.applyPreHandle(processedRequest, response)) {
            return;
         }

         // 实际调用处理程序。
         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);
      }
       //处理处理程序选择和处理程序调用的结果,该结果可以是ModelAndView,也可以是要解析为ModelAndView的异常。
      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);
         }
      }
   }
}

3、处理器获取

doDispatch()方法将获取的请求,在处理器映射集中找到最优解,然后将获取的handler与一些拦截器封装之后在返回,封装后的对象

1、getHandler()
//该方法来自:DispatcherServlet.class
//返回此请求的HandlerExecutionChain。按顺序尝试所有处理程序映射。
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;
}
//AbstractHandlerMapping类
/**
     * AbstractHandlerMapping类是接口HandlerMapping(处理器映射)的抽象实现
     * 查找给定请求的处理程序,如果找不到特定的处理程序,则返回默认处理程序
     */
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    //查找给定请求的处理程序,如果找不到特定的处理程序,则返回null。此方法由getHandler调用;如果设置了空的返回值,则返回默认处理程序。
    //通过调用该方法,咋就获得了对应的处理器。重点内容在下面细说:2、处理器
   Object handler = getHandlerInternal(request);
   if (handler == null) {
      handler = getDefaultHandler();
   }
   if (handler == null) {
      return null;
   }
   // Bean name or resolved handler?
   if (handler instanceof String) {
      String handlerName = (String) handler;
      handler = obtainApplicationContext().getBean(handlerName);
   }

   // Ensure presence of cached lookupPath for interceptors and others
    //确保存在拦截器和其他拦截器的缓存lookupPath
   if (!ServletRequestPathUtils.hasCachedPath(request)) {
       //初始化用于请求映射的路径。
      initLookupPath(request);
   }
/**
     * getHandlerExecutionChain(handler, request):此方法会将用户请求(也就是handler)
     *      会将它(也就是handler)与所有的interceptors封装为HandlerExecutionChain对象
     * interceptors包含:
     *      1、conversionService:数据转换的拦截器,因为一般情况下MVC中的V在用户的客户端,而C则在服务端,
     *      当客户端向服务端提交数据时,不能再以java中的对象形式进行传输了,这时候就需要序列化和反序列化来帮助实现了
     *      2、resourceUrlProvider:用于获取客户端访问静态资源时应使用的公共URL路径的中心组件。
     *      此类知道用于服务静态资源的Spring MVC处理程序映射,
     *      并使用已配置ResourceHttpRequestHandler的ResourceResolver链来做出决策。
     *      3、....
     */
   HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

   if (logger.isTraceEnabled()) {
      logger.trace("Mapped to " + handler);
   }
   else if (logger.isDebugEnabled() && !DispatcherType.ASYNC.equals(request.getDispatcherType())) {
      logger.debug("Mapped to " + executionChain.getHandler());
   }
//判断该处理是不是夸资源共享
   if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
      CorsConfiguration config = getCorsConfiguration(handler, request);
      if (getCorsConfigurationSource() != null) {
         CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
         config = (globalConfig != null ? globalConfig.combine(config) : config);
      }
      if (config != null) {
         config.validateAllowCredentials();
      }
      executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
   }

   return executionChain;
}
2、getHandlerInternal()
//来自RequestMappingInfoHandlerMapping.java
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
   request.removeAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
   try {
      return super.getHandlerInternal(request);
   }
   finally {
      ProducesRequestCondition.clearMediaTypesAttribute(request);
   }
}
//来自AbstractHandlerMethodMapping.java
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    //初始化用于请求映射的路径。
   String lookupPath = initLookupPath(request);
    //使用getMappings和getMappingsByUrl时获取读锁。保证线程安全
   this.mappingRegistry.acquireReadLock();
   try {
       //(重点)获取处理方法
      HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
       //handlerMethod.createWithResolvedBean():如果提供的实例包含bean名称而不是对象实例,则在创建并返回HandlerMethod之前解析bean名称。
      return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
   }
   finally {
       //结束后释放锁
      this.mappingRegistry.releaseReadLock();
   }
}
3、lookupHandlerMethod()
//来自AbstractHandlerMethodMapping.java
//查找当前请求的最佳匹配处理程序方法。如果找到多个匹配项,则选择最佳匹配项。
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
   List<Match> matches = new ArrayList<>();
    //返回给定URL路径的匹配项。不是线程安全的。
   List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
   if (directPathMatches != null) {
       //通过获取的路径,找到对应的处理方法,并将其添加到匹配映射集中
      addMatchingMappings(directPathMatches, matches, request);
   }
   if (matches.isEmpty()) {
      addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
   }
   if (!matches.isEmpty()) {
      Match bestMatch = matches.get(0);
       //如果你获取了两个匹配的处理器,就会报错,下面就是关于这个的逻辑实现。
      if (matches.size() > 1) {
         Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
         matches.sort(comparator);
         bestMatch = matches.get(0);
         if (logger.isTraceEnabled()) {
            logger.trace(matches.size() + " matching mappings: " + matches);
         }
         if (CorsUtils.isPreFlightRequest(request)) {
            for (Match match : matches) {
               if (match.hasCorsConfig()) {
                  return PREFLIGHT_AMBIGUOUS_MATCH;
               }
            }
         }
         else {
            Match secondBestMatch = matches.get(1);
            if (comparator.compare(bestMatch, secondBestMatch) == 0) {
               Method m1 = bestMatch.getHandlerMethod().getMethod();
               Method m2 = secondBestMatch.getHandlerMethod().getMethod();
               String uri = request.getRequestURI();
               throw new IllegalStateException(
                     "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
            }
         }
      }
       //向request添加相关的属性,把关于处理器方法以及映射地址通通添加到request中,方便获取
      request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
      handleMatch(bestMatch.mapping, lookupPath, request);
      return bestMatch.getHandlerMethod();
   }
   else {
       //这是没有找到相关处理器对应的手段
      return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
   }
}

4、适配器获取

我直接解析调用handler不就行了为啥还要使用HandlerAdapter包装一层呢?

这就不得不说说handler的类型:

  • 以实现了Controller接口的Handler类
  • 以@RequestMapping注解修饰的HandlerMethod对象
  • 其他还有实现Servlet的实例,HandlerFunction实例、HttpRequestHandler实例等

不同的实例对象调用时走不同的方法,为了能将不同的方法转换成统一的调用形式,这里使用了适配器模式,将各个实例的方法调用包装

到HandlerAdapter统一调用

public interface HandlerAdapter {

/**
     * 该方法判断该适配器类是否支持解析调用Handler对象,因为Adapter被设计成了一个只适配执行某一种类型比如Controller接口类型或
     *
     * @RequestMapping类型的Handler需要使用SimpleControllerHandlerAdapter来进行处理,该方法在执行handler()方法之前需要使用该
     *
     * 方法来判断是否支持调用这种类型的Handler对象。
     */
   boolean supports(Object handler);

  
    //解析并调用Handler对象的方法执行业务逻辑,从request请求中获取参数,执行handler并将响应结果放入reponse对象
   @Nullable
   ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

 /**
     * 获取请求的资源的最终修改时间,如果请求的资源未被修改则直接使用浏览器缓存,这个方法主要是针对http请求为
     *  get/head请求为提高性能的缓存处理。
     */
   @Deprecated
   long getLastModified(HttpServletRequest request, Object handler);

}

当获取返回的处理器后,将其交于对应的处理器适配器处理。

//来自DispatcherServlet.java
//返回此处理程序对象的HandlerAdapter。
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    //首先自然是判断处理器适配器是否为空
   if (this.handlerAdapters != null) {
      for (HandlerAdapter adapter : this.handlerAdapters) {
          //判断是否支持,支持则返回该适配器
         if (adapter.supports(handler)) {
            return adapter;
         }
      }
   }
   throw new ServletException("No adapter for handler [" + handler +
         "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

5、视图解析

然后会执行ha.handle(),返回一个ModelAndView的对象。

具体实现:

//来自AbstractHandlerMethodAdapter.java
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {

   return handleInternal(request, response, (HandlerMethod) handler);
}
//来自RequestMappingHandlerAdapter.java
protected ModelAndView handleInternal(HttpServletRequest request,
      HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

   ModelAndView mav;
    //如果有的话,检查给定的请求以获得支持的方法和所需的会话
   checkRequest(request);

   //如果需要,在同步块中执行invokeHandlerMethod。
   if (this.synchronizeOnSession) {
      HttpSession session = request.getSession(false);
      if (session != null) {
         Object mutex = WebUtils.getSessionMutex(session);
         synchronized (mutex) {
            mav = invokeHandlerMethod(request, response, handlerMethod);
         }
      }
      else {
         // No HttpSession available -> no mutex necessary
         mav = invokeHandlerMethod(request, response, handlerMethod);
      }
   }
   else {
      // No synchronization on session demanded at all...
      mav = invokeHandlerMethod(request, response, handlerMethod);
   }

   if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
      if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
         applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
      }
      else {
         prepareResponse(response);
      }
   }

   return mav;
}

在上述源码中,最重要的一行就是

mav = invokeHandlerMethod(request, response, handlerMethod);

在该方法中,会调用invokeAndHandle():此方法的就是来处理请求的

//来自ServletInvocableHandlerMethod.java
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
//通过请求,获取对应的处理器处理,然后将处理获取的值返回。
		Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
		setResponseStatus(webRequest);

		if (returnValue == null) {
			if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
				disableContentCachingIfNecessary(webRequest);
				mavContainer.setRequestHandled(true);
				return;
			}
		}
		else if (StringUtils.hasText(getResponseStatusReason())) {
			mavContainer.setRequestHandled(true);
			return;
		}

		mavContainer.setRequestHandled(false);
		Assert.state(this.returnValueHandlers != null, "No return value handlers");
		try {
            //将返回的结果处理渲染后,返回到服务器中
			this.returnValueHandlers.handleReturnValue(
					returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
		}
		catch (Exception ex) {
			if (logger.isTraceEnabled()) {
				logger.trace(formatErrorForReturnValue(returnValue), ex);
			}
			throw ex;
		}
	}
//HandlerMethodReturnValueHandlerComposite.java
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
      ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
//获取对应的处理器
   HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
   if (handler == null) {
      throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
   }
    //进行处理
   handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
//RequestResponseBodyMethodProcessor.java
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
      ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
      throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

   mavContainer.setRequestHandled(true);
   ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
   ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

   // Try even with null return value. ResponseBodyAdvice could get involved.
   writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}

通过调用writeWithMessageConverters(),程序会获取我们请求链接的响应值,以及该值的类型…

然后将响应值经过渲染后返回给用户。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值