我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Spring MVC请求参数的深入解析

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Spring MVC请求参数的深入解析

请求参数解析

客户端请求在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对应的适配器,处理参数映射、执行控制器逻辑、返回值处理的核心源码处理。

总结

到此这篇关于Spring MVC请求参数解析的文章就介绍到这了,更多相关SpringMVC请求参数内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Spring MVC请求参数的深入解析

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

深入理解Spring MVC的数据转换

本文主要给大家介绍了关于Spring MVC数据转换的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧。数据绑定SpringMVC负责将request中的信息以一定的方式转换并绑定到处理方法的参数上。整个过程的处理核
2023-05-31

Spring MVC怎么实现GET请求接收Date类型参数

这篇“Spring MVC怎么实现GET请求接收Date类型参数”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Spring
2023-07-02

Spring Boot下如何校验Spring MVC的请求参数及如何自定义校验注解

Spring Boot提供了spring-boot-starter-validation 为Bean的校验提供支持。我们可以通过一系列的校验注解对Java Bean的属性进行校验。

深入浅析Spring MVC中的URL映射规则

今天就跟大家聊聊有关深入浅析Spring MVC中的URL映射规则,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。url-pattern
2023-05-31

深入分析 SpringMVC 参数解析器

HandlerMethodArgumentResolver 就是我们口口声声说的参数解析器,它的实现类还是蛮多的,因为每一种类型的参数都对应了一个参数解析器。

深度解析:Elasticsearch 写入请求处理流程

对于 Elasticsearch 写入一篇文档相信大家不陌生,但是Elasticsearch 的底层究竟是如何处理的呢,接下来让我们一起来一探究竟。

POST请求和GET请求如何传递和接收解析参数

接口参数应该怎么传递是每个项目应该面对的问题,这跟编程语言无关,今天来总结一波常用的接口参数传递方式。
POSTGET参数2024-12-10

如何进行JSONP跨域请求原理的深入解析

这篇文章将为大家详细讲解有关如何进行JSONP跨域请求原理的深入解析,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。什么是同源策略同源策略,它是由Netscape提出的一个著名的安全策略。现在
2023-06-26

深度解析Spring AI:请求与响应机制的核心逻辑

在这次探讨中,我们聚焦于Spring AI如何有效地发起请求并将响应信息传递给用户。这一过程不仅是开发者与AI交互的桥梁,更是优化用户体验的关键。通过明确的请求结构和响应机制,Spring AI能够灵活地处理各种用户输入,并根据上下文调整回
SpringAI策略2024-11-29

Kotlin + Spring Boot 请求参数验证的代码实例

编写 Web 应用程序的时候,经常要做的事就是要对前端传回的数据进行简单的验证,比如是否非空、字符长度是否满足要求,邮箱格式是否正确等等。在 Spring Boot 中,可以使用 Bean Validation (JSR-303) 技术通过
2023-05-31

Spring MVC参数自动绑定List的解决方法

最近遇到一个问题,在项目中需要传入一组对象。以前都只遇到过传入一个对象、多个对象或者一组数组的情况。很少遇到传入一组对象的情况。所以遇到这个问题的时候感觉有点棘手。本以为用这样的方法就能解决的。@RequestMapping("save1"
2023-05-30

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录