springmvc@RequestBody原理及扩展

编程入门 行业动态 更新时间:2024-10-28 04:28:42

springmvc@RequestBody<a href=https://www.elefans.com/category/jswz/34/1770123.html style=原理及扩展"/>

springmvc@RequestBody原理及扩展

深入理解@RequestBody原理

一、RequestBody注解概述

我们通常使用RequestBody注解来web请求中的body体部分内容,并且通常会将其转换为一个javaBean的对象!

如下面的示例文件:

    @PostMapping("/school")public School querySchool(@RequestBody School school){return school;}
@Data
public class School {private String name;
}

示例对象依赖的POM文件为:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns=".0.0" xmlns:xsi=""xsi:schemaLocation=".0.0 .0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.6.3</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.example</groupId><artifactId>rsa-sign-demo</artifactId><version>0.0.1-SNAPSHOT</version><name>rsa-sign-demo</name><description>rsa-sign-demo</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.apachemons</groupId><artifactId>commons-lang3</artifactId></dependency><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.4</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>2.6.3</version><executions><execution><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build>
</project>

通过Postman发送请求,得到结果如下:

那么springMVC是如何将Body体中的内容绑定到实体对象School中去的呢?

requestBody注解类注释:

Annotation indicating a method parameter should be bound to the body of the web request.
The body of the request is passed through an {@link HttpMessageConverter} to resolve the method argument depending on the content type of the request.

从上面的描述我们可以看到主要是通过HttpMessageConverter来进行转换,

二、执行流程

所有的springmvc请求都会经过org.springframework.web.servlet.DispatcherServlet#doDispatch来进行处理,我们就从该方法开始跟踪。主要流程图如下所示:

2.1、DispatcherServlet处理

doDispatch方法中的源码如下,只保留我们想进行关注的部分代码:

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 {processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);// Determine handler for the current request.//根据请求来来获取对应的处理器mappedHandler = getHandler(processedRequest);if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}// Determine handler adapter for the current request.//根据处理器获取到处理器对应的适配器HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// 省略部分代码 ...if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// Actually invoke the handler.// 这里使用RequestMappingHandlerAdapter来处理请求mv = ha.handle(processedRequest, response, mappedHandler.getHandler());// 省略部分代码 ...}//省略部分代码 ...}

2.2、AbstractHandlerMethodAdapter的handle方法

RequestMappingHandlerAdapter是AbstractHandlerMethodAdapter的子类,调用的是其父类的handle方法

	@Override@Nullablepublic final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {return handleInternal(request, response, (HandlerMethod) handler);}

子类RequestMappingHandlerAdapter的handleInternal方法实现,具体代码如下:

@Overrideprotected 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 necessarymav = 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;}

接下来跟踪org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod方法

@Nullableprotected ModelAndView invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {// 省略部分代码...try {// 省略部分代码...ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);if (this.argumentResolvers != null) {invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);}if (this.returnValueHandlers != null) {invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);}// 省略部分代码// 调用ServletInvocableHandlerMethod的invokeAndHandleinvocableMethod.invokeAndHandle(webRequest, mavContainer);if (asyncManager.isConcurrentHandlingStarted()) {return null;}return getModelAndView(mavContainer, modelFactory, webRequest);}finally {webRequest.requestCompleted();}}

2.3、ServletInvocableHandlerMethod#invokeAndHandle

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {// 执行请求方法调用Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);setResponseStatus(webRequest);//省略部分代码 ....mavContainer.setRequestHandled(false);Assert.state(this.returnValueHandlers != null, "No return value handlers");try {// 处理结果返回this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);}//省略部分代码 ....}

在invokeAndHandle方法中,主要干两个事情

  • invokeForRequest执行方法参数绑定、执行方法调用,获取返回值
  • this.returnValueHandlers.handleReturnValue 对controller层中的方法返回的结果进行处理

接下来我们继续跟踪invokeForRequest方法:

2.4、InvocableHandlerMethod#invokeForRequest

	@Nullablepublic 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);}

invokeForRequest中做了两件事情

  • 1、通过getMethodArgumentValues方法获取请求参数
  • 2、通过doInvoke使用反射进行controller层方法的调用

然后我们接下来跟踪getMethodArgumentValues方法:

2.5、getMethodArgumentValues方法源码

// InvocableHandlerMethod.java
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++) {//省略部分代码....//使用参数解析器来解析对应的参数,这里是HandlerMethodArgumentResolverCompositeargs[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);//省略部分代码....}return args;}

getMethodArgumentValues方法中的主要逻辑是:

  • 1、获取即将调用的controller层方法对应的参数,如果没有参数直接进行返回
  • 2、调用参数解析器(HandlerMethodArgumentResolverComposite)来执行参数的解析

接下来的任务是跟踪HandlerMethodArgumentResolverComposite如果从请求中解析对应的参数出来:

2.6 resolveArgument源码跟踪

// HandlerMethodArgumentResolverComposite.java
@Override@Nullablepublic 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.");}// 使用RequestResponseBodyMethodProcessor来解析对应的参数return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);}

resolveArgument方法的主要作用是:

  • 1、根据对应方法和参数获取支持的方法及参数解析器,这里获取的是RequestResponseBodyMethodProcessor实例对象
  • 2、使用RequestResponseBodyMethodProcessor解析方法参数

接下来我们继续跟踪RequestResponseBodyMethodProcessor的resolveArgument方法

2.7、RequestResponseBodyMethodProcessor#resolveArgument

	@Overridepublic Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {parameter = parameter.nestedIfOptional();// 从httpInputMessage中来读取请求体信息Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());// 省略部分代码...return adaptArgumentIfNecessary(arg, parameter);}

resolveArgument方法方法主要部分就是调用了readWithMessageConverters方法

2.8、readWithMessageConverters方法解析

// RequestResponseBodyMethodProcessor.java
@Overrideprotected <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");// 创建一个HttpInputMessage对象ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);//调用另一个重载方法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、基于原始请求的Request构造一个ServletServerHttpRequest对象
  • 2、调用重载的readWithMessageConverters方法来执行真正的参数绑定

2.9、readWithMessageConverters方法解析

// RequestResponseBodyMethodProcessor.java
@Nullableprotected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {// 省略部分代码 ...Object body = NO_VALUE;AbstractMessageConverterMethodArgumentResolver.EmptyBodyCheckingHttpInputMessage message = null;message = new AbstractMessageConverterMethodArgumentResolver.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()) {//执行所有符合条件的RequestBodyAdvice对应的beforeBodyRead方法HttpInputMessage msgToUse =getAdvice().beforeBodyRead(message, parameter, targetType, converterType);//使用MappingJackson2HttpMessageConverter消息转换器来读取对应的消息body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));//执行所有符合条件的RequestBodyAdvice对应的afterBodyRead方法body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);}else {body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);}break;}}// 省略部分代码 ...return body;}

该方法中主要有三件事情进行处理

  • 1、调用RequestResponseBodyAdviceChain的beforeBodyRead来修改原始的HttpInputMessage对象
  • 2、调用MappingJackson2HttpMessageConverte的read方法来进行数据的读取,这里入参中HttpInputMessage对象为上一步中返回的
  • 3、调用RequestResponseBodyAdviceChain的afterBodyRead方法来修改第二步中返回的body

我们继续来回到org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest方法中,来看下获取到参数入参后的处理方法

2.10、invokeForRequest方法执行真正controller层调用

	@Nullablepublic 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);}

该方法主要是使用上面所讲通过MappingJackson2HttpMessageConverte从Request中绑定出来的参数对象进行controller层的方法调用

接下来跟踪下获取到结果返回值后,如何对结果进行处理的,org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle

2.11、invokeAndHandle返回值逻辑处理

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {// 省略部分代码...this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);//省略部分代码...}

这里我们核心关注下this.returnValueHandlers.handleReturnValue方法的执行,这个是HandlerMethodReturnValueHandlerComposite对象

2.12、handleReturnValue源码跟踪

//HandlerMethodReturnValueHandlerComposite#handleReturnValue
@Overridepublic void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {// 根据返回的值及返回值类型来选择符合条件的HandlerMethodReturnValueHandler对象HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);if (handler == null) {throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());}//使用前面获取到的实例来处理返回的结果值handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);}

上述方法逻辑很简单,主要是干两个事情

  • 1、根据返回的结果和MethodParameter对象来到对应的返回值处理器对象
  • 2、使用第一步获取到的对象对返回的值进行真正处理

2.13、selectHandler方法解析

//HandlerMethodReturnValueHandlerComposite.java	
@Nullableprivate HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {boolean isAsyncValue = isAsyncReturnValue(value, returnType);for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {continue;}if (handler.supportsReturnType(returnType)) {return handler;}}return null;}

上述方法的逻辑的是依次遍历HandlerMethodReturnValueHandlerComposite中所有的HandlerMethodReturnValueHandler对象,一旦有支持的那么就会返回该处理器,遍历完后如果还是没有找到那么就返回null对象。我们这里使用的是RequestResponseBodyMethodProcessor对象,org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#supportsReturnType

// RequestResponseBodyMethodProcessor.java
@Overridepublic boolean supportsReturnType(MethodParameter returnType) {return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||returnType.hasMethodAnnotation(ResponseBody.class));}

从上面的代码可以看出来,当类层级或者方法层级上有@ResponseBody注解的时候,将会使用RequestResponseBodyMethodProcessor来进行处理

我们来继续跟踪handleReturnValue方法

2.14、handleReturnValue方法解析

// RequestResponseBodyMethodProcessor.java	
@Overridepublic void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest)throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {mavContainer.setRequestHandled(true);ServletServerHttpRequest inputMessage = createInputMessage(webRequest);ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);// Try even with null return value. ResponseBodyAdvice could get involved.writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);}

该方法的核心的就是调用了writeWithMessageConverters方法,接下来跟踪下该方法

2.15、writeWithMessageConverters方法源码解析

protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {// 省略部分代码 ...if (selectedMediaType != null) {selectedMediaType = selectedMediaType.removeQualityValue();for (HttpMessageConverter<?> converter : this.messageConverters) {GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?(GenericHttpMessageConverter<?>) converter : null);//判断当前的HttpMessageConverter是否能够写入该对象if (genericConverter != null ?((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :converter.canWrite(valueType, selectedMediaType)) {// 请求体进行写入的前置处理body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,(Class<? extends HttpMessageConverter<?>>) converter.getClass(),inputMessage, outputMessage);if (body != null) {Object theBody = body;LogFormatUtils.traceDebug(logger, traceOn ->"Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");addContentDispositionHeader(inputMessage, outputMessage);if (genericConverter != null) {//使用MappingJackson2HttpMessageConverter对象进行写入genericConverter.write(body, targetType, selectedMediaType, outputMessage);}else {((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);}}else {if (logger.isDebugEnabled()) {logger.debug("Nothing to write: null body");}}return;}}}// 省略部分代码 ...}

至此,我们分析完了所有整个流程的代码!

三、源码涉及的到的重点类以及方法总结

3.1 涉及的接口及类

  • 1、HandlerMethodArgumentResolver接口: 用来对一个request进行参数解析的接口,如我们使用@RequestBody注解的时候,会使用RequestResponseBodyMethodProcessor来进行处理参数

  • 2、RequestResponseBodyMethodProcessor:HandlerMethodArgumentResolver接口的实现类,主要是用来处理在方法参数添加了@RequestBody注解的赋值操作以及在方法上添加了@RequestBody的返回值的处理

  • 3、HttpMessageConverter接口: 用来将请求体和响应体进行转换的接口

    将请求转换为接口入参,将http处理方法的返回值转换为http响应。

  • 4、MappingJackson2HttpMessageConverter实现类:

    HttpMessageConverter接口的实现类,用来读取和写入JSON格式类型的请求

  • 5、RequestBodyAdvice: 可以对使用@RequestBody注释的参数进行修改

  • 6、ResponseBodyAdvice:对使用了@ResponseBody注释的方法或类,用来对请求进行修改

}
``

至此,我们分析完了所有整个流程的代码!

三、源码涉及的到的重点类以及方法总结

3.1 涉及的接口及类

  • 1、HandlerMethodArgumentResolver接口: 用来对一个request进行参数解析的接口,如我们使用@RequestBody注解的时候,会使用RequestResponseBodyMethodProcessor来进行处理参数

  • 2、RequestResponseBodyMethodProcessor:HandlerMethodArgumentResolver接口的实现类,主要是用来处理在方法参数添加了@RequestBody注解的赋值操作以及在方法上添加了@RequestBody的返回值的处理

  • 3、HttpMessageConverter接口: 用来将请求体和响应体进行转换的接口

    将请求转换为接口入参,将http处理方法的返回值转换为http响应。

  • 4、MappingJackson2HttpMessageConverter实现类:

    HttpMessageConverter接口的实现类,用来读取和写入JSON格式类型的请求

  • 5、RequestBodyAdvice: 可以对使用@RequestBody注释的参数进行修改

  • 6、ResponseBodyAdvice:对使用了@ResponseBody注释的方法或类,用来对请求进行修改

更多推荐

springmvc@RequestBody原理及扩展

本文发布于:2023-12-04 18:41:18,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1661869.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:原理   springmvc   RequestBody

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!