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

精彩内容推送,请关注公众号!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值