【Spring】@RequestBody的实现原理

这篇具有很好参考价值的文章主要介绍了【Spring】@RequestBody的实现原理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

@RequestBody注解可以用于POST请求接收请求体中的参数,使用方式如下:

@Controller
public class IndexController {
    
    @PostMapping(value = "/submit", produces = MediaType.APPLICATION_JSON_VALUE)
    public void submit(@RequestBody UserInfo userInfo) {
        System.out.println(userInfo.toString());
    }
}

那么是如何从请求中解析数据设置到对应的参数中呢,接下来就从源码的角度一探究竟。

DispatcherServlet是Spring MVC的核心,它对请求进行调度,收到请求后会进入DispatcherServletdoDispatch方法中:

  1. 调用getHandler方法获取请求对应的Handler处理器;
  2. 根据handler获取对应的适配器,这里用到了适配器模式;
  3. 调用适配器的handle方法处理请求,它会返回一个ModelAndView对象;
public class DispatcherServlet extends FrameworkServlet {
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

		try {
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
				// 检查是否有Multipart
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// 根据请求获取对应的处理器
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// 根据handler获取对应的适配器
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// ...

				// 处理请求
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				// ...
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			catch (Throwable err) {
				dispatchException = new NestedServletException("Handler dispatch failed", err);
			}
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		// ...
	}
}

通过POSTMAN模拟请求,在代码中打断点可以看到HandlerAdapter的类型为对RequestMappingHandlerAdapter:
【Spring】@RequestBody的实现原理

handle方法在其父类AbstractHandlerMethodAdapter中实现,在它的handle方法中,又调用了handleInternal方法处理请求,handleInternal是一个抽象方法,由具体的子类实现:

public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {
	@Override
	@Nullable
	public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
	    // 处理请求
		return handleInternal(request, response, (HandlerMethod) handler);
	}

	@Nullable
	protected abstract ModelAndView handleInternal(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception;
}

所以回到RequestMappingHandlerAdapterhandleInternal方法,里面调用了invokeHandlerMethod方法进行处理:

  1. 创建ServletInvocableHandlerMethod
  2. 调用invokeAndHandle方法继续请求处理;
  3. 调用getModelAndView方法返回ModelAndView;
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
		implements BeanFactoryAware, InitializingBean {
    @Override
	protected ModelAndView handleInternal(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		ModelAndView mav;
		checkRequest(request);
		if (this.synchronizeOnSession) {
			HttpSession session = request.getSession(false);
			if (session != null) {
				Object mutex = WebUtils.getSessionMutex(session);
				synchronized (mutex) {
					// 执行请求
					mav = invokeHandlerMethod(request, response, handlerMethod);
				}
			}
			else {
				// 执行请求
				mav = invokeHandlerMethod(request, response, handlerMethod);
			}
		}
		else {
			// 执行请求
			mav = invokeHandlerMethod(request, response, handlerMethod);
		}
		// ...
		return mav;
	}

	@Nullable
	protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		try {
			// ...
            // 创建ServletInvocableHandlerMethod
            ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
			// 调用invokeAndHandle方法处理请求
			invocableMethod.invokeAndHandle(webRequest, mavContainer);
			if (asyncManager.isConcurrentHandlingStarted()) {
				return null;
			}
			// 返回ModelAndView
			return getModelAndView(mavContainer, modelFactory, webRequest);
		}
		finally {
			webRequest.requestCompleted();
		}
	}
}

ServletInvocableHandlerMethodinvokeAndHandle中调用了invokeForRequest方法执行请求,它的实现在其父类InvocableHandlerMethod中:

public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
    public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
    	// 执行请求
		Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
		setResponseStatus(webRequest);

		// ...
	}
}

invokeForRequest中又调用了getMethodArgumentValues方法获取请求中的参数,处理逻辑如下:

  1. 调用getMethodParameters获取方法中的参数,也就是我们的请求处理器方法中的所有参数,上面看到submit只接收了一个UserInfo类型的参数,这里可以从断点中看到parameters中只有一个元素,类型为UserInfo:
    【Spring】@RequestBody的实现原理

  2. 对获取到方法中的所有参数进行遍历,通过处理器调用resolveArgument方法解析请求中的数据,解析每一个参数对应的值;

public class InvocableHandlerMethod extends HandlerMethod {
	@Nullable
	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);
			// ...
			try {
				// 调用resolveArgument从请求中解析对应的数据
				args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
			}
			// ...
		}
		return args;
	}
}

resolveArgument方法在HandlerMethodArgumentResolverComposite中实现:

  1. 调用getArgumentResolver方法获取对应的参数处理器resolver

  2. 调用resolverresolveArgument方法进行参数解析;

public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
	@Override
	@Nullable
	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);
	}
}

从断点中可以看到此时的resolverRequestResponseBodyMethodProcessor类型的:
【Spring】@RequestBody的实现原理

进入到RequestResponseBodyMethodProcessorresolveArgument方法中,它又调用了readWithMessageConverters方法解析参数,最终会进入到
AbstractMessageConverterMethodArgumentResolve中的readWithMessageConverters方法:

public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
    @Override
	public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

		parameter = parameter.nestedIfOptional();
		// 通过转换器进行参数解析
		Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
		String name = Conventions.getVariableNameForParameter(parameter);
		// ...
		return adaptArgumentIfNecessary(arg, parameter);
	}

	@Override
	protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
			Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

		HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
		Assert.state(servletRequest != null, "No HttpServletRequest");
		ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
		// 调用AbstractMessageConverterMethodArgumentResolver中readWithMessageConverters方法读取参数
		Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
		if (arg == null && checkRequired(parameter)) {
			throw new HttpMessageNotReadableException("Required request body is missing: " +
					parameter.getExecutable().toGenericString(), inputMessage);
		}
		return arg;
	}
}

readWithMessageConverters方法处理逻辑如下:

  1. 遍历所有HTTP消息转换器,判断是否支持解析当前的请求参数类型;

  2. 如果转换器支持解析当前的参数类型并且有消息体内容,调用转换器的read方法进行解析;

public abstract class AbstractMessageConverterMethodArgumentResolver implements HandlerMethodArgumentResolver {
    @Nullable
	protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
			Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

		// ...
		try {
			message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
			// 遍历所有的消息转换器
			for (HttpMessageConverter<?> converter : this.messageConverters) {
				Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
				GenericHttpMessageConverter<?> genericConverter =
						(converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
				// 判断是否支持当前参数类型的读取
				if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
						(targetClass != null && converter.canRead(targetClass, contentType))) {
					// 如果有消息体
					if (message.hasBody()) {
						HttpInputMessage msgToUse =
								getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
						// 调用read方法进行读取
						body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
								((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
						body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
					}
					else {
						body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
					}
					break;
				}
			}
		}
		catch (IOException ex) {
			throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);
		}

		// ...
		return body;
	}
}

这里列举一些消息转换器的类型:
【Spring】@RequestBody的实现原理

对于application/json;charset=UTF-8类型会进入到MappingJackson2HttpMessageConverterread方法在其父类AbstractJackson2HttpMessageConverter,处理逻辑如下:

  1. 获取参数的Class类型,从断点中可以看出是[class com.example.demo.model.UserInfo];
    【Spring】@RequestBody的实现原理

  2. 调用readJavaType方法解析参数
    (1)获取ContentType,前面可以看到请求接收的类型为application/json;
    (2)获取字符集,这里的字符集为UTF-8;
    (3)创建ObjectMapper对象,并从请求体中读取JSON数据,转为JAVA对象;

public abstract class AbstractJackson2HttpMessageConverter extends AbstractGenericHttpMessageConverter<Object> {

    @Override
	public Object read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)
			throws IOException, HttpMessageNotReadableException {
		// 获取参数的Class类型
		JavaType javaType = getJavaType(type, contextClass);
		// 解析参数
		return readJavaType(javaType, inputMessage);
	}

	private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) throws IOException {
		// 获取ContentType
		MediaType contentType = inputMessage.getHeaders().getContentType();
		// 获取字符集
		Charset charset = getCharset(contentType);
		ObjectMapper objectMapper = selectObjectMapper(javaType.getRawClass(), contentType);
		Assert.state(objectMapper != null, "No ObjectMapper for " + javaType);
		boolean isUnicode = ENCODINGS.containsKey(charset.name());
		try {
		    // ...
			if (isUnicode) {
				// 获取HTTP请求体中的JSON数据,转为JAVA对象
				return objectMapper.readValue(inputMessage.getBody(), javaType);
			}
			else {
				Reader reader = new InputStreamReader(inputMessage.getBody(), charset);
				return objectMapper.readValue(reader, javaType);
			}
		}
		// ....
	}
}

到这里已经成功从HTTP请求体中的JSON数据,并转为JAVA对象,完成了参数的设置。

Spring版本:5.3.4文章来源地址https://www.toymoban.com/news/detail-592558.html

到了这里,关于【Spring】@RequestBody的实现原理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 深入解析 Spring Framework 中 @Autowired 注解的实现原理

    关于@Autowired注解的作用 @Autowired 注解在Spring中的作用是实现依赖注入(Dependency Injection),它用于自动装配(autowiring)Spring Bean 的依赖关系。具体来说, @Autowired 注解有以下作用: 自动装配依赖 :通过在类的字段、构造函数、方法参数等地方使用 @Autowired 注解,Spring 容器会

    2024年02月08日
    浏览(40)
  • Spring Boot中的@Scheduled注解:定时任务的原理与实现

    本文将详细探讨Spring Boot中@Scheduled注解的使用,包括其原理、实现流程、步骤和代码示例。通过本文,读者将能够了解如何在Spring Boot应用中轻松创建和管理定时任务。 在Spring框架中,@Scheduled注解用于标记一个方法,使其能够在固定的时间间隔内自动执行。这个注解主要基于

    2024年02月22日
    浏览(45)
  • 【深入浅出Spring原理及实战】「夯实基础系列」360全方位渗透和探究Spring的核心注解开发和实现指南(Spring5的常见的注解)

    Spring 5.x中常见的注解包括@Controller、@Service、@Repository。当我们研究Spring Boot源码时,会发现实际上提供了更多的注解。了解这些注解对于我们非常重要,尽管目前可能还用不到它们。 注解 功能 @Bean 器中注册组件,代替来的标签 @Configuration 声明这是一个配置类,替换以前的配

    2024年02月16日
    浏览(46)
  • post请求可以不加@RequestBody注解么?使用与不使用@RequestBody注解的区别

    @RequestBody只能处理json格式的数据。 使用@RequestBody注解时,用于接收Content-Type为application/json类型的请求,数据类型是JSON:{“aaa”:“111”,“bbb”:“222”} 不加@RequestBody可以支持表单的默认格式,但是不能处理json格式的数据(在restTemplete进行远程服务调用时,是以json格式进行实体

    2024年02月12日
    浏览(46)
  • @RequestBody注解基础

    @RequestBody @RequestBody注解一般与post方法使用。 一个请求中只能存在一个@RequestBody注解。 @RequestBody 用于接收前端传递给后端的json字符串中的数据。(处理json格式的数据) 语法格式: ​(@RequestBody Map map) (@RequestBody Object object) (1)(@RequestBody Map map)方式: 这种方式相对来说比较

    2024年01月21日
    浏览(47)
  • 深入剖析@RequestBody、@PathVariable和@RequestParam注解

    当我们在开发服务端方法时,遇到给方法传参的有几个不同的注解,今天我们来介绍 @RequestBody 、 @PathVariable 和 @RequestParam 这几个注解的定义和使用场景示例,以便于同学们理解和掌握。 @RequestBody 注解: 定义: @RequestBody 注解用于从请求体中获取数据,并将其转换为指定的对

    2024年02月09日
    浏览(37)
  • Spring @Autowired 注解原理

    被扫描的组件配置类 输出结果 定位@Autowired所在包 org.springframework.beans.factory.annotation.Autowired 找到同包下 AutowiredAnnotationBeanPostProcessor AutowiredAnnotationBeanPostProcessor 的类继承图如下 AutowiredAnnotationBeanPostProcessor实现了InstantiationAwareBeanPostProcessor与 MergedBeanDefinitionPostProcessor两个Bea

    2024年02月16日
    浏览(42)
  • Spring Boot 学习之——@SpringBootApplication注解(自动注解原理)

    springboot是基于spring的新型的轻量级框架,最厉害的地方当属**自动配置。**那我们就可以根据启动流程和相关原理来看看,如何实现传奇的自动配置 从上面代码可以看出,Annotation定义(@SpringBootApplication)和类定义(SpringApplication.run)最为耀眼,所以要揭开SpringBoot的神秘面纱

    2024年01月25日
    浏览(49)
  • 关于postman传递两个参数@RequestBody注解只能有一个!

    1.一个bean一个集合,集合前面必须加注解  postman的传参写法  ========================================================================= 2.一个bean和一个数组,数组前面是不用加注解的也可以接收的上 postman传参    

    2024年02月11日
    浏览(35)
  • Spring Boot 中@EnableAutoConfiguration注解原理

    开启  Spring  的自动装配功能; 简单点说就是Spring Boot根据依赖中的jar包,自动选择 实例化 某些配置,配置类必须有@Configuration注解。 二、@EnableAutoConfiguration的原理 1.点进启动类上的EnableAutoConfiguration注解 2.发现@EnableAutoConfiguration注解实际上也是@Import注解实现的(其实@Ena

    2024年01月17日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包