SpringMVC请求参数解析
请求参数解析
客户端请求在handlerMapping中找到对应handler后,将会继续执行DispatchServlet的doPatch()方法。
首先是找到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请求参数解析的更多相关文章
- springmvc 请求参数解析细节
springmvc 的请求流程,相信大家已经很熟悉了,不熟悉的同学可以参考下资料! 有了整体流程的概念,是否对其中的实现细节就很清楚呢?我觉得不一定,比如:单是参数解析这块,就是个大学问呢? 首先,我 ...
- SpringMVC请求参数接收总结
前提 在日常使用SpringMVC进行开发的时候,有可能遇到前端各种类型的请求参数,这里做一次相对全面的总结.SpringMVC中处理控制器参数的接口是HandlerMethodArgumentRes ...
- SpringMVC请求参数接收总结(一)
前提 在日常使用SpringMVC进行开发的时候,有可能遇到前端各种类型的请求参数,这里做一次相对全面的总结.SpringMVC中处理控制器参数的接口是HandlerMethodArgumentRes ...
- SpringMVC请求参数总结
前提 在日常使用SpringMVC进行开发的时候,有可能遇到前端各种类型的请求参数,这里做一次相对全面的总结.SpringMVC中处理控制器参数的接口是HandlerMethodArgumentRes ...
- 2.5万字长文简单总结SpringMVC请求参数接收
这是公众号<Throwable文摘>发布的第22篇原创文章,暂时收录于专辑<架构与实战>.暂定下一篇发布的长文是<图文分析JUC同步器框架>,下一篇发布的短文是&l ...
- springmvc请求参数异常统一处理
1.ExceptionHandlerController package com.oy.controller; import java.text.MessageFormat; import org.s ...
- SpringBoot系列教程web篇之Post请求参数解析姿势汇总
作为一个常年提供各种Http接口的后端而言,如何获取请求参数可以说是一项基本技能了,本篇为<190824-SpringBoot系列教程web篇之Get请求参数解析姿势汇总>之后的第二篇,对 ...
- SpringBoot系列教程web篇之Get请求参数解析姿势汇总
一般在开发web应用的时候,如果提供http接口,最常见的http请求方式为GET/POST,我们知道这两种请求方式的一个显著区别是GET请求的参数在url中,而post请求可以不在url中:那么一个 ...
- springmvc请求参数异常统一处理,结合钉钉报告信息定位bug位置
参考之前一篇博客:springmvc请求参数异常统一处理 1.ExceptionHandlerController package com.oy.controller; import java.tex ...
随机推荐
- 痞子衡嵌入式:自识别特性(Auto Probe)可以让i.MXRT1060无需FDCB也能从NOR Flash启动
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是自识别特性(Auto Probe)可以让i.MXRT1060无需FDCB也能从NOR Flash启动. 接着上篇文章 <了解i.M ...
- Java基础语法:final修饰符
一.final类 描述: 用'final'修饰的类不能被继承,没有子类. 例如,我们是无法写一个类去继承String类,然后对String类型扩展的,因为API中已经将String类定义为'final ...
- 读懂一个中型的Django项目
转自https://www.cnblogs.com/huangfuyuan/p/Django.html [前言]中型的项目是比较多的APP,肯会涉及多数据表的操作.如果有人带那就最好了,自己要先了解基 ...
- 看完我的笔记不懂也会懂----ECMAscript 567
目录 ECMAscript 567 严格模式 字符串扩展 数值的扩展 Object对象方法扩展 数组的扩展 数组方法的扩展 bind.call.apply用法详解 let const 变量的解构赋值 ...
- Java数组之选择排序
选择排序 package com.kangkang.array; import java.util.Arrays; public class demo04 { public static void m ...
- Wireguard 全互联模式(full mesh)配置指南
上篇文章给大家介绍了如何使用 wg-gen-web 来方便快捷地管理 WireGuard 的配置和秘钥,文末埋了两个坑:一个是 WireGuard 的全互联模式(full mesh),另一个是使用 W ...
- 破解MySQL库user表hash密码
目录 得到用户名和密码 hash 带*和不带*的区别 破解hash 在线工具 Hashcat 实验环境 select version(); 得到用户名和密码 hash mysql安装好就会默认生成图中 ...
- Linux:使用systemd管理进程
Blog:博客园 个人 概述 systemd是目前Linux系统上主要的系统守护进程管理工具,由于init一方面对于进程的管理是串行化的,容易出现阻塞情况,另一方面init也仅仅是执行启动脚本,并不能 ...
- 仿MSDN的帮助系统
作为软件开发人员,软件做好后,接下来就是编写文档.我自己也是做软件的,经常有用户询问软件的安装与使用, 我一直很喜欢微软的MSDN帮助系统,简介.大气,使用方便. 网上也找了很久,感觉一直没有合适的, ...
- PTA 带头结点的链式表操作集
6-2 带头结点的链式表操作集 (20 分) 本题要求实现带头结点的链式表操作集. 函数接口定义: List MakeEmpty(); Position Find( List L, Element ...