SpringMvc源码分析4.2——HandlerAdapter


一. 准备

接着上一篇,我们暴露的接口:

@RestController
public class IndexController {
    @GetMapping("/hello")
    public String hello(String name) {
        return "hello: " + name;
    }
}

bebug到DispatcherServlet#doDispatch

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 1. 关注点一
				mappedHandler = getHandler(processedRequest);
				
				// 2. 关注点二
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

			  // 3. todo待分析,关注点三
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  
  			// 4. todo待分析,关注点四
				applyDefaultViewName(processedRequest, mv);
			
  			// 5. todo待分析,关注点五
 			  processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
	
	}

二. 分析

1. 关注几个属性值

我们先看一下DispatcherServlet内部几个属性此时的值:

/** List of HandlerMappings used by this servlet. */
	@Nullable
	private List<HandlerMapping> handlerMappings;

	/** List of HandlerAdapters used by this servlet. */
	@Nullable
	private List<HandlerAdapter> handlerAdapters;

	/** List of HandlerExceptionResolvers used by this servlet. */
	@Nullable
	private List<HandlerExceptionResolver> handlerExceptionResolvers;

	/** RequestToViewNameTranslator used by this servlet. */
	@Nullable
	private RequestToViewNameTranslator viewNameTranslator;
	
/** List of ViewResolvers used by this servlet. */
	@Nullable
	private List<ViewResolver> viewResolvers;
  • handlerMappings handlerMappings

  • handlerAdapters

  • handlerExceptionResolvers handlerExceptionResolvers

  • viewNameTranslatorviewNameTranslator

  • viewResolversviewResolvers

2. 关注点三源码

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

执行具体的handler并返回ModelAndView对象;从Post not found: SpringMvc源码分析4.1——HandlerAdapter SpringMvc源码分析4.1——HandlerAdapter可以知道,这里的haRequestMappingHandlerAdapter。顺序执行链我们最终定位到RequestMappingHandlerAdapter#handleInternal,源码如下:

protected ModelAndView handleInternal(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		ModelAndView mav;
  	// 校验请求的method和是否需要session
		checkRequest(request);

		// Execute invokeHandlerMethod in synchronized block if required.
  	// 这里有一个配置,因为session不是线程安全的,这里可以配置使其同步执行
		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...
      // todo待分析
			mav = invokeHandlerMethod(request, response, handlerMethod);
		}

  	// 删减
		....
		return mav;
	}

该方法里面首先进行了一个校验checkRequest主要是校验请求的method和是否需要session。然后做了一个是否需要在session上的同步判断。该synchronizeOnSession默认值为false的,默认Session是有线程安全问题的,如果这个值设置为true而且有session的话,就会同步执行invokeHandlerMethod(),从而避免session的线程安全问题。接着,再调用mav = invokeHandlerMethod(request, response, handlerMethod);执行handler

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		try {
      // 包含了对@InitBinder的处理
			WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
      // 包含了对@ModelAttribute的处理
			ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

      // 设置参数解析器,参数名解析等
			ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
			if (this.argumentResolvers != null) {
				invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
			}
			if (this.returnValueHandlers != null) {
				invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
			}
			invocableMethod.setDataBinderFactory(binderFactory);
			invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

			ModelAndViewContainer mavContainer = new ModelAndViewContainer();
			mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
			modelFactory.initModel(webRequest, mavContainer, invocableMethod);
			mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

      // 删除异步请求的处理代码
			...

      // 调用方法并通过配置的HandlerMethodReturnValueHandlers之一来处理返回值。重点关注
			invocableMethod.invokeAndHandle(webRequest, mavContainer);
			if (asyncManager.isConcurrentHandlingStarted()) {
				return null;
			}

      // ModelAndView
			return getModelAndView(mavContainer, modelFactory, webRequest);
		}
		finally {
			webRequest.requestCompleted();
		}
	}

到这,我们先来看一下RequestMappingHandlerAdapter里面的几个属性此时的值:

 // 自定义参数解析器,会后于内置的参数解析器
@Nullable
private List<HandlerMethodArgumentResolver> customArgumentResolvers;

// 参数解析器
@Nullable
private HandlerMethodArgumentResolverComposite argumentResolvers;

 // 在{@code @InitBinder}方法中配置支持的参数类型
@Nullable
private HandlerMethodArgumentResolverComposite initBinderArgumentResolvers;

// 自定义返回值处理器,会后于内置的返回值处理器
@Nullable
private List<HandlerMethodReturnValueHandler> customReturnValueHandlers;

 // 返回值处理器
@Nullable
private HandlerMethodReturnValueHandlerComposite returnValueHandlers;

 // 消息转换器
private List<HttpMessageConverter<?>> messageConverters;
 
 // RequestBodyAdvice或者ResponseBodyAdvice
 private List<Object> requestResponseBodyAdvice = new ArrayList<>();
  • customArgumentResolvers

​ 此时为空,没有自定义

  • argumentResolvers

argumentResolvers

  • initBinderArgumentResolvers

initBinderArgumentResolvers

  • customReturnValueHandlers

​ 为空,没有自定义

  • returnValueHandlers

returnValueHandlers

  • messageConverters

messageConverters

  • requestResponseBodyAdvice

requestResponseBodyAdvice

从源码上看,我们需要重点关注一下invocableMethod.invokeAndHandle(webRequest, mavContainer);,如下:

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 {
      // 重点关注,调用匹配的HandlerMethodReturnValueHandler
      this.returnValueHandlers.handleReturnValue(
            returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
   }
   catch (Exception ex) {
      if (logger.isTraceEnabled()) {
         logger.trace(formatErrorForReturnValue(returnValue), ex);
      }
      throw ex;
   }
}

这个方法,里面就包括了handler方法参数的解析,方法的处理,返回值的处理等,这需要我们重点分析。分析这一块我们能看到@RequestParam@RequestBody@ResponseBody等的处理过程。内容有点多,放到下一节单独分析。

三. 总结

到此,我们大概分析了一下ha.handle(processedRequest, response, mappedHandler.getHandler())的执行逻辑。这其中有很多复杂的逻辑,由于我们的测试请求很简单,项目也没有做什么其他的配置,所以很多流程代码执行不到。但是没关系,我们从简单的做起,也能了解得到SpringMvc处理请求一个大概的流程。OK,回顾一下,这一节主要了解到了:

  1. synchronizeOnSession的作用;
  2. 参数值的解析,需要用到argumentResolverstodo分析
  3. 返回值的主力需要用到returnValueHandlerstodo分析

文章作者: shiv
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 shiv !
评论
  目录