请求参数解析

客户端请求在handlerMapping中找到对应handler后,将会继续执行DispatchServletdoPatch()方法。

首先是找到handler对应的适配器。

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

进入到getHandlerAdapter(mappedHandler.getHandler())方法中

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");
}

这里存在多个适配器,如图:

其中使用@RequestMaping注解修饰的控制器都将适配第一个适配器;而函数式方法将会使用第二个适配器。

跟踪请求,这里将会获得第一个适配器,判断也简单,如下:

public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}

如果是HandlerMethod类型的处理器就采用这个适配器,而客户端请求正好对应的是HandlerMethod处理器。

找到适配器后,将会真正执行处理器逻辑。如下:

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

进入RequestMappingHandlerAdapter,执行适配器核心方法:

@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ModelAndView mav;
checkRequest(request); // Execute invokeHandlerMethod in synchronized block if required.
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);

同样,我们打开RequestMappingHandlerAdapter中的invokeHandlerMethod方法:

@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
//通过处理器获得真正的执行方法及其参数列表
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
//给执行方法对象添加参数解析器
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
//给执行方法对象添加返回值处理器
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
//处理器对象装配完成,执行控制器方法
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
} return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}

在这个关键方法中,首先执行请求对应的控制器逻辑,之后进行系列处理,根据返回值处理器处理返回值。

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;
}
}

以下给出部分参数解析器及返回值处理器截图:

参数解析器。对应每一个参数(路径变量、矩阵变量、获得请求头、请求域等)的获取方式

返回值处理器。ModelAndView、ResponseBody等。每个处理器处理不同类别的返回值类型。

接下来,真正进入到最终执行method方法invocableMethod.invokeAndHandle(webRequest, mavContainer);,这里是真是执行控制器中映射的方法。

以下为获得参数列表对应值的逻辑,参数获取完成后将会执行真正的控制器逻辑。

public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//通过参数解析器获取参数列表每一个参数的值
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
return doInvoke(args);
}

进入解析逻辑,解析是对比每一个参数绑定的注解,如果注解一致将会使用对应的解析器将请求传递的参数值获取到。

protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//获得控制器参数列表,每一个参数包含其参数类型,参数次序、注解修饰(用于使用对应解析器)
MethodParameter[] parameters = getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
} 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) {
// Leave stack trace for later, exception may actually be resolved and handled...
if (logger.isDebugEnabled()) {
String exMsg = ex.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw ex;
}
}
return args;
}

查看解析器是否指出当前参数部分代码,可以了解到SpringMVC的缓存策略。

private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
//从缓存中获取当前参数的解析器
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
//缓存中不存在则将这个参数对应的解析器加到缓存中,提升后续相同请求响应速度。
if (result == null) {
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
if (resolver.supportsParameter(parameter)) {
result = resolver;
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}

参数列表

以下为获得的参数列表第一个参数部分属性

其对应的是控制器中id参数:

@GetMapping(value = "/student01/{id}/car/{name}")
public Map<String, Object> testAnnotation(@PathVariable(name = "id") String id){
Map<String, Object> map = new HashMap<>();
map.put("id", id);
return map;
}

以上即为获得请求Handler对应的适配器,处理参数映射、执行控制器逻辑、返回值处理的核心源码处理。

SpringMVC请求参数解析的更多相关文章

  1. springmvc 请求参数解析细节

    springmvc 的请求流程,相信大家已经很熟悉了,不熟悉的同学可以参考下资料! 有了整体流程的概念,是否对其中的实现细节就很清楚呢?我觉得不一定,比如:单是参数解析这块,就是个大学问呢? 首先,我 ...

  2. SpringMVC请求参数接收总结

    前提 在日常使用SpringMVC进行开发的时候,有可能遇到前端各种类型的请求参数,这里做一次相对全面的总结.SpringMVC中处理控制器参数的接口是HandlerMethodArgumentRes ...

  3. SpringMVC请求参数接收总结(一)

    前提 在日常使用SpringMVC进行开发的时候,有可能遇到前端各种类型的请求参数,这里做一次相对全面的总结.SpringMVC中处理控制器参数的接口是HandlerMethodArgumentRes ...

  4. SpringMVC请求参数总结

    前提 在日常使用SpringMVC进行开发的时候,有可能遇到前端各种类型的请求参数,这里做一次相对全面的总结.SpringMVC中处理控制器参数的接口是HandlerMethodArgumentRes ...

  5. 2.5万字长文简单总结SpringMVC请求参数接收

    这是公众号<Throwable文摘>发布的第22篇原创文章,暂时收录于专辑<架构与实战>.暂定下一篇发布的长文是<图文分析JUC同步器框架>,下一篇发布的短文是&l ...

  6. springmvc请求参数异常统一处理

    1.ExceptionHandlerController package com.oy.controller; import java.text.MessageFormat; import org.s ...

  7. SpringBoot系列教程web篇之Post请求参数解析姿势汇总

    作为一个常年提供各种Http接口的后端而言,如何获取请求参数可以说是一项基本技能了,本篇为<190824-SpringBoot系列教程web篇之Get请求参数解析姿势汇总>之后的第二篇,对 ...

  8. SpringBoot系列教程web篇之Get请求参数解析姿势汇总

    一般在开发web应用的时候,如果提供http接口,最常见的http请求方式为GET/POST,我们知道这两种请求方式的一个显著区别是GET请求的参数在url中,而post请求可以不在url中:那么一个 ...

  9. springmvc请求参数异常统一处理,结合钉钉报告信息定位bug位置

    参考之前一篇博客:springmvc请求参数异常统一处理 1.ExceptionHandlerController package com.oy.controller; import java.tex ...

随机推荐

  1. 痞子衡嵌入式:自识别特性(Auto Probe)可以让i.MXRT1060无需FDCB也能从NOR Flash启动

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是自识别特性(Auto Probe)可以让i.MXRT1060无需FDCB也能从NOR Flash启动. 接着上篇文章 <了解i.M ...

  2. Java基础语法:final修饰符

    一.final类 描述: 用'final'修饰的类不能被继承,没有子类. 例如,我们是无法写一个类去继承String类,然后对String类型扩展的,因为API中已经将String类定义为'final ...

  3. 读懂一个中型的Django项目

    转自https://www.cnblogs.com/huangfuyuan/p/Django.html [前言]中型的项目是比较多的APP,肯会涉及多数据表的操作.如果有人带那就最好了,自己要先了解基 ...

  4. 看完我的笔记不懂也会懂----ECMAscript 567

    目录 ECMAscript 567 严格模式 字符串扩展 数值的扩展 Object对象方法扩展 数组的扩展 数组方法的扩展 bind.call.apply用法详解 let const 变量的解构赋值 ...

  5. Java数组之选择排序

    选择排序 package com.kangkang.array; import java.util.Arrays; public class demo04 { public static void m ...

  6. Wireguard 全互联模式(full mesh)配置指南

    上篇文章给大家介绍了如何使用 wg-gen-web 来方便快捷地管理 WireGuard 的配置和秘钥,文末埋了两个坑:一个是 WireGuard 的全互联模式(full mesh),另一个是使用 W ...

  7. 破解MySQL库user表hash密码

    目录 得到用户名和密码 hash 带*和不带*的区别 破解hash 在线工具 Hashcat 实验环境 select version(); 得到用户名和密码 hash mysql安装好就会默认生成图中 ...

  8. Linux:使用systemd管理进程

    Blog:博客园 个人 概述 systemd是目前Linux系统上主要的系统守护进程管理工具,由于init一方面对于进程的管理是串行化的,容易出现阻塞情况,另一方面init也仅仅是执行启动脚本,并不能 ...

  9. 仿MSDN的帮助系统

    作为软件开发人员,软件做好后,接下来就是编写文档.我自己也是做软件的,经常有用户询问软件的安装与使用, 我一直很喜欢微软的MSDN帮助系统,简介.大气,使用方便. 网上也找了很久,感觉一直没有合适的, ...

  10. PTA 带头结点的链式表操作集

    6-2 带头结点的链式表操作集 (20 分)   本题要求实现带头结点的链式表操作集. 函数接口定义: List MakeEmpty(); Position Find( List L, Element ...