Spring MVC请求参数的深入解析
作者:是冯吉荣呀 发布时间:2021-11-26 22:55:25
请求参数解析
客户端请求在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对应的适配器,处理参数映射、执行控制器逻辑、返回值处理的核心源码处理。
总结
来源:https://www.cnblogs.com/fjrgg/p/14545595.html


猜你喜欢
- java 中基本算法之希尔排序的实例详解希尔排序(Shell Sort)是插入排序的一种。也称缩小增量排序,是直接插入排序算法的一种更高效的
- 在我们移动应用开发过程中,偶尔有可能会接到这种需求:1、在手机桌面创建一个窗口,类似于360的悬浮窗口,点击这个窗口可以响应(至于窗口拖动我
- 本文实例为大家分享了C#实现网页画图的具体代码,供大家参考,具体内容如下代码贴着保存下using System;using System.C
- 前言相信做地图社交类APP开发的大家都知道,一般情况下,为了整体的美观和用户体验度,我们需要定制化Marker的样式。本文中实现的方式都是基
- service是业务层 action层即作为控制器DAO (Data Access Object) 数据访问1.JAVA中Action层,
- 1.在外部开启activity时需要新开一个task,从service里开启activity时出现了这个异常。W/System.err: a
- 本文为大家分享了C#导入导出Excel数据的具体代码,供大家参考,具体内容如 * :对于实体类对象最好新建一个并且继承原有实体类,这样可以将类
- 前言在日常开发中,圆形的图片效果还是很常见的。可以通过给Paint设置Xfermode来实现,这里简单记录如下。实现实现圆形效果的核心是Po
- 本文介绍了Spring Boot 与DBunit 配合使用方法,分享给大家,具体如下:DBUnit快速上手Springboot 添加 DBu
- 本文实例讲述了C#中参数个数可变的方法。分享给大家供大家参考。具体方法如下:要实现C#中参数个数可变关键是使用params关键字。并且,可变
- 1.概览该教程中,我将向你展示:如何在测试时设置spring boot 日志级别。虽然我们可以在测试通过时忽略日志,但是如果需要诊断失败的测
- 几种序列化技术:1)二进制序列化保持类型保真度,这对于在应用程序的不同调用之间保留对象的状态很有用。例如,通过将对象序列化到剪贴板,可在不同
- 定义:封装某些作用于某种数据结构中各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。类型:行为类模式类图:例子:例如
- 问题原因Springboot get请求是参数过长抛出异常:Request header is too large 的问题错误描述java.
- 通常我们遇到的图片缩放需求,都是图片基于屏幕自适应后,进行缩放和移动,且图片最小只能是自适应的大小。最近遇到一个需求,要求图片只能在屏幕内缩
- 简介今天的课程开始进入高级课程类了,我们要开始接触网络协议、设备等领域编程了。在今天的课程里我们会使用OKHttp组件来访问网络资源而不是使
- protected 来谈谈protected访问权限问题。看下面示例1:Test.javaclass MyObject {}public c
- 本文实例讲述了C#数据结构之堆栈(Stack)。分享给大家供大家参考,具体如下:堆栈(Stack)最明显的特征就是“先进后出”,本质上讲堆栈
- 前言本文主要介绍的是关于CentOS 7配置自定义JDK的方法教程,分享出来供大家参考学习,下面来一起看看详细的介绍:配置教程由于选择的是C
- 1、环境搭建创建一个SpringBoot项目,普通的web项目就可以了,我这里使用的是start.aliyun引入依赖:(1)老演员了不多说