Spring-MVC【源码篇】请求参数和响应结果解析

版本:spring Boot 2.1.3.RELEASE 相当于 Spring Framework web 5.1.5.RELEASE

回顾

在 Spring MVC 中讲解了 Spring MVC 的工作机制。

第一步、 请求 Request 进入到 Spring 应用中,统一由分发器 DispatcherServlet 进行接收处理。

第二步、 DispatcherServlet 根据请求路径,从 HandlerMapping 中获取对应的 controller ,并将结果返回为 DispatcherServlet。

第三步、 DispatcherServlet 根据 RequestMapping 返回的 Handler 去 HandlerAdapter 中获取对应适配的结果返回给 DispatcherServlet,如:ModelAndView

第四步、 DispatcherServlet 根据返回的 ModelAndView 到 ViewResolver 中进行解析,最后得到响应 View

下面我们来看几个简单 Spring MVC 使用Demo

@Controller
public class SpringMvcController {

    @RequestMapping("/sys/request-body")
    @ResponseBody
    public SysDict requestBody(@RequestBody SysDict sysDict){
        return sysDict;
    }

    @RequestMapping("/sys/obj")
    @ResponseBody
    public SysDict obj(SysDict sysDict){
        return sysDict;
    }

    @RequestMapping("/sys/request-param")
    @ResponseBody
    public String requestParam(@RequestParam String sysDict){
        return sysDict;
    }

    @RequestMapping("/sys/date")
    @ResponseBody
    public Date testDate(@DateTimeFormat(pattern = "yyyy-HH-mm HH:mm:ss") Date date){
        return date;
    }
}

以上的几个接口中,各自方法接收了不同的参数类型,并通过譬如:@RequestParam 、@DateTimeFormat 等方式对参数进行解析赋值。

那么 Spring MVC 在请求参数与方法绑定前如何对参数进行解析?且在返回结果后对结果集进行解析?

通过 Spring MVC 的工作流程,能够直接的猜测Spring MVC 参数解析赋值的操作在 第三步 HandlerAdapter 的执行过程中。

源码解析 Spring MVC 在请求参数与方法绑定前如何对参数进行解析?且在返回结果后对结果集进行解析?

Spring MVC 构造 ServletInvocableHandlerMethod

在 Spring MVC 中 HttpRequestHandlerAdapter 是全局的请求适配器,针对每一次请求,Spring MVC 会构造 ServletInvocableHandlerMethod 进行请求调用。
而 请求参数解析器集合 和 响应结果解析器集合 在构造 ServletInvocableHandlerMethod 时会作为属性设置到 ServletInvocableHandlerMethod 中。

聚焦 DispatcherServlet#doDispatch

public class DispatcherServlet extends FrameworkServlet {
	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {

		// Determine handler adapter for the current request.
		// 获取 HandlerAdapter
		HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

		// Actually invoke the handler.
		// 执行 HandlerAdapter#handle
		mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

	}
}

Spring MVC 中 DispatcherServlet 进行请求处理的主要两个步骤:

  • 步骤一:获取 HandlerAdapter (默认实现:HttpRequestHandlerAdapter)
  • 步骤二:执行 HandlerAdapter#handle

下面我们来看看 默认实现:HttpRequestHandlerAdapter#handler 方法的实现

HttpRequestHandlerAdapter#handler

通过查看源码能够发现 HttpRequestHandlerAdapter#handler 方法在其抽象父类 AbstractHandlerMethodAdapter 中实现,而父类 AbstractHandlerMethodAdapter#handler 真实调用的是 AbstractHandlerMethodAdapter#handleInternal 而该方法由其子类进行实现,也就是 HttpRequestHandlerAdapter#handleInternal 。而该方法中,主要的执行逻辑方法为 HttpRequestHandlerAdapter#invokeHandlerMethod

聚焦 HttpRequestHandlerAdapter#invokeHandlerMethod

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializingBean {
	protected ModelAndView invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
		ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
		// 请求数据解析类集合
		if (this.argumentResolvers != null) {
			invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
		}
		// 响应数据解析类集合
		if (this.returnValueHandlers != null) {
			invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
		}
	}
}

在该方法中对请求执行对象 ServletInvocableHandlerMethod 进行属性赋值,其中主要的两个属性对象:

  • 一、请求参数解析器集合
  • 二、响应结果解析器集合

请求参数解析

解析器集合

查看 RequestMappingHandlerAdapter 中 argumentResolvers 数据类型。

HandlerMethodArgumentResolverComposite argumentResolvers;

通过名字能够猜测 argumentResolvers 是一个 方法参数解析器的集合。

查看 HandlerMethodArgumentResolverComposite 信息验证猜想

public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
	private final List<HandlerMethodArgumentResolver> argumentResolvers = new LinkedList<>();

	public HandlerMethodArgumentResolverComposite addResolver(HandlerMethodArgumentResolver resolver) { ...... }

	public HandlerMethodArgumentResolverComposite addResolvers(@Nullable HandlerMethodArgumentResolver... resolvers) { ...... }

	public HandlerMethodArgumentResolverComposite addResolvers(@Nullable List<? extends HandlerMethodArgumentResolver> resolvers) { ...... }

	public List<HandlerMethodArgumentResolver> getResolvers() { ...... }

	public void clear() { ...... }

	public Object resolveArgument( ......){ ...... }

	private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) { ...... }
}

代码信息:

1、 HandlerMethodArgumentResolverComposite 中维护了一个集合 argumentResolvers 用来存放 HandlerMethodArgumentResolver 集合。
2、 针对 方法参数解析器集合 HandlerMethodArgumentResolverComposite 提供了增删改查的方法。
3、 HandlerMethodArgumentResolverComposite 中主要的执行方法为 HandlerMethodArgumentResolverComposite#resolveArgumentHandlerMethodArgumentResolverComposite#getArgumentResolver

在分析 HandlerMethodArgumentResolverComposite 是如何进行参数配对解析前,我们先来看看 方法参数解析器 的接口定义。

聚焦 方法参数解析器 接口定义 HandlerMethodArgumentResolver

// @since 3.1
public interface HandlerMethodArgumentResolver {
	// 判断方法参数,是否符合解析条件
	boolean supportsParameter(MethodParameter parameter);
	// 进行解析
	Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory);
}

方法参数解析 接口定义很明显,就两个方法:

  • HandlerMethodArgumentResolver#supportsParameter
    判定方法参数是否满足此解析器的条件

  • HandlerMethodArgumentResolver#resolveArgument
    进行参数解析

回到 HandlerMethodArgumentResolverComposite 来看看参数解析过程

public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {

	// 执行解析
	public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

		HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
		if (resolver == null) {
			throw new IllegalArgumentException(
					"Unsupported parameter type [" + parameter.getParameterType().getName() + "]." +
							" supportsParameter should be called first.");
		}
		return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
	}

	// 获取匹配的解析执行器
	private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
		HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
		if (result == null) {
			for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {
				if (methodArgumentResolver.supportsParameter(parameter)) {
					result = methodArgumentResolver;
					this.argumentResolverCache.put(parameter, result);
					break;
				}
			}
		}
		return result;
	}
}

代码一目了然,遍历 HandlerMethodArgumentResolver 集合 argumentResolvers ,逐个调用 HandlerMethodArgumentResolver#supportsParameter 判定当前 MethodParameter 是否有匹配的 HandlerMethodArgumentResolver 。 如果存在匹配的 HandlerMethodArgumentResolver 返回,并执行调用 HandlerMethodArgumentResolver#resolveArgument。

解析器如何生效

再次回到 HttpRequestHandlerAdapter#invokeHandlerMethod 方法。

该方法构建 ServletInvocableHandlerMethod 对象并设置相关参数,包括 请求参数解析器集合 HandlerMethodArgumentResolverComposite ,在环境和参数准备完成之后,调用 ServletInvocableHandlerMethod#invokeAndHandle 进行方法请求调用。

通过追踪 ServletInvocableHandlerMethod#invokeAndHandle -> InvocableHandlerMethod#invokeForRequest -> InvocableHandlerMethod#getMethodArgumentValues

聚焦InvocableHandlerMethod#getMethodArgumentValues

public class InvocableHandlerMethod extends HandlerMethod {

	private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();
	public void setHandlerMethodArgumentResolvers(HandlerMethodArgumentResolverComposite argumentResolvers) {
		this.resolvers = argumentResolvers;
	}

	// 获取方法参数集
	protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {

		if (ObjectUtils.isEmpty(getMethodParameters())) {
			return EMPTY_ARGS;
		}
		MethodParameter[] parameters = getMethodParameters();
		Object[] args = new Object[parameters.length];
		// 遍历每个参数
		for (int i = 0; i < parameters.length; i++) {
			MethodParameter parameter = parameters[i];
			parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
			args[i] = findProvidedArgument(parameter, providedArgs);
			if (args[i] != null) {
				continue;
			}
			if (!this.resolvers.supportsParameter(parameter)) {
				throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
			}
			try {
				// 针对每个参数进行解析
				args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
			}
			catch (Exception ex) { ...... }
		}
		return args;
	}
}

在上面的 InvocableHandlerMethod 代码中,其中的属性 resolvers 就是前面的解析器集合。

上面的代码同样一目了然,遍历每个参数,针对每个参数进行解析器匹配,然后进行解析。

响应结果解析

解析器集合

在上面的请求响应解析中,请求响应解析器 HandlerMethodArgumentResolverComposite 存放在了 ServletInvocableHandlerMethod*(其父类InvocableHandlerMethod)* resolvers 属性中。

而在响应结果解析中,响应结果解析器集合 HandlerMethodReturnValueHandlerComposite 存放在 ServletInvocableHandlerMethod returnValueHandlers 属性中。

我们一样来看看 HandlerMethodReturnValueHandlerComposite 响应结果解析器集合的相关信息

public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler {
	private final List<HandlerMethodReturnValueHandler> returnValueHandlers = new ArrayList<>();

	public HandlerMethodReturnValueHandlerComposite addHandler(HandlerMethodReturnValueHandler handler) { ...... }

	public HandlerMethodReturnValueHandlerComposite addHandlers( @Nullable List<? extends HandlerMethodReturnValueHandler> handlers) { ...... }

	public List<HandlerMethodReturnValueHandler> getHandlers() { ...... }

	boolean supportsReturnType(MethodParameter returnType){ ...... }

	void handleReturnValue( ...... ) throws Exception { ...... }
}

1、 在 HandlerMethodReturnValueHandlerComposite 中维护了 returnValueHandlers 集合,用来存放所有的 响应结果解析器
2、 在 HandlerMethodReturnValueHandlerComposite 中提供了增加,获取 响应结果解析器的方法。

在查看 HandlerMethodReturnValueHandlerComposite 的具体调用之前,我们一样先来看看 响应结果解析器的规范定义 HandlerMethodReturnValueHandler。

public interface HandlerMethodReturnValueHandler {
	boolean supportsReturnType(MethodParameter returnType);

	void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}

在接口规范定义中,定义了两个方法

  • HandlerMethodReturnValueHandler#supportsReturnType
    响应结果是否符合解析器,解析条件

  • HandlerMethodReturnValueHandler#handleReturnValue
    通过向模型添加属性并将视图或标志设置为true来处理给定的返回值,以指示直接处理了响应。

回到 HandlerMethodArgumentResolverComposite 来看看具体的处理方法

public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler {
	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);
	}

	private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
		boolean isAsyncValue = isAsyncReturnValue(value, returnType);
		for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
			if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
				continue;
			}
			if (handler.supportsReturnType(returnType)) {
				return handler;
			}
		}
		return null;
	}

}

HandlerMethodArgumentResolverComposite 执行的方法和 请求参数处理的 HandlerMethodArgumentResolverComposite 处理方式相类似,通过遍历集合 returnValueHandlers,
调用各个响应处理器的 HandlerMethodReturnValueHandler#supportsReturnType 方法判定,是否有满足的 HandlerMethodReturnValueHandler 响应结果解析器,如果有,则调用相关的 HandlerMethodReturnValueHandler#handleReturnValue 方法进行执行解析。

解析器如何生效

再次回到 HttpRequestHandlerAdapter#invokeHandlerMethod 方法。

该方法构建 ServletInvocableHandlerMethod 对象并设置相关参数,包括 请求参数解析器集合 HandlerMethodArgumentResolverComposite ,在环境和参数准备完成之后,调用 ServletInvocableHandlerMethod#invokeAndHandle 进行方法请求调用。

通过追踪 ServletInvocableHandlerMethod#invokeAndHandle -> InvocableHandlerMethod#invokeForRequest -> InvocableHandlerMethod#getMethodArgumentValues 我们可以找到 请求参数解析器的解析过程。
在请求参数解析完成之后,会进行参数与方法的绑定,之后执行 InvocableHandlerMethod#doInvoke 得到相应结果,而返回结果在 ServletInvocableHandlerMethod#invokeAndHandle 中被处理

聚焦 ServletInvocableHandlerMethod#invokeAndHandle

public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
	public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
		// 请求参数通过解析器解析之后执行方法调用,并返回结果
		Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);

		// 结果集处理
		this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
	}
}

总结

[外链图片转存失败(img-yk7iuDEI-1565930113104)(/img/technological_course/spring/springframework/Spring-MVC【源码篇】请求参数和响应结果解析_总结.png)]
Spring-MVC【源码篇】请求参数和响应结果解析_第1张图片

Spring MVC 依赖于 Servlet 容器进行启动,所有请求的处理都有 DispatcherServlet 进行处理。
在请求参数相关格式的处理上,Spring MVC 通过一串 HandlerMethodArgumentResolver 进行处理。
在响应结果值相关格式处理上,Spring MVC 通过一串 HandlerMethodReturnValueHandler 进行处理。

精彩内容推送,请关注公众号!
Spring-MVC【源码篇】请求参数和响应结果解析_第2张图片

你可能感兴趣的:(微信公众号同步)