【Spring】@RequestBody的实现原理

打印 上一主题 下一主题

主题 885|帖子 885|积分 2655

@RequestBody注解可以用于POST请求接收请求体中的参数,使用方式如下:
  1. @Controller
  2. public class IndexController {
  3.    
  4.     @PostMapping(value = "/submit", produces = MediaType.APPLICATION_JSON_VALUE)
  5.     public void submit(@RequestBody UserInfo userInfo) {
  6.         System.out.println(userInfo.toString());
  7.     }
  8. }
复制代码
那么是如何从请求中解析数据设置到对应的参数中呢,接下来就从源码的角度一探究竟。
DispatcherServlet是Spring MVC的核心,它对请求进行调度,收到请求后会进入DispatcherServlet的doDispatch方法中:

  • 调用getHandler方法获取请求对应的Handler处理器;
  • 根据handler获取对应的适配器,这里用到了适配器模式;
  • 调用适配器的handle方法处理请求,它会返回一个ModelAndView对象;
  1. public class DispatcherServlet extends FrameworkServlet {
  2.     protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
  3.                 HttpServletRequest processedRequest = request;
  4.                 HandlerExecutionChain mappedHandler = null;
  5.                 boolean multipartRequestParsed = false;
  6.                 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
  7.                 try {
  8.                         ModelAndView mv = null;
  9.                         Exception dispatchException = null;
  10.                         try {
  11.                                 // 检查是否有Multipart
  12.                                 processedRequest = checkMultipart(request);
  13.                                 multipartRequestParsed = (processedRequest != request);
  14.                                 // 根据请求获取对应的处理器
  15.                                 mappedHandler = getHandler(processedRequest);
  16.                                 if (mappedHandler == null) {
  17.                                         noHandlerFound(processedRequest, response);
  18.                                         return;
  19.                                 }
  20.                                 // 根据handler获取对应的适配器
  21.                                 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
  22.                                 // ...
  23.                                 // 处理请求
  24.                                 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  25.                                 // ...
  26.                         }
  27.                         catch (Exception ex) {
  28.                                 dispatchException = ex;
  29.                         }
  30.                         catch (Throwable err) {
  31.                                 dispatchException = new NestedServletException("Handler dispatch failed", err);
  32.                         }
  33.                         processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
  34.                 }
  35.                 // ...
  36.         }
  37. }
复制代码
通过POSTMAN模拟请求,在代码中打断点可以看到HandlerAdapter的类型为对RequestMappingHandlerAdapter:

handle方法在其父类AbstractHandlerMethodAdapter中实现,在它的handle方法中,又调用了handleInternal方法处理请求,handleInternal是一个抽象方法,由具体的子类实现:
  1. public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {
  2.         @Override
  3.         @Nullable
  4.         public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
  5.                         throws Exception {
  6.             // 处理请求
  7.                 return handleInternal(request, response, (HandlerMethod) handler);
  8.         }
  9.         @Nullable
  10.         protected abstract ModelAndView handleInternal(HttpServletRequest request,
  11.                         HttpServletResponse response, HandlerMethod handlerMethod) throws Exception;
  12. }
复制代码
所以回到RequestMappingHandlerAdapter的handleInternal方法,里面调用了invokeHandlerMethod方法进行处理:

  • 创建ServletInvocableHandlerMethod;
  • 调用invokeAndHandle方法继续请求处理;
  • 调用getModelAndView方法返回ModelAndView;
  1. public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
  2.                 implements BeanFactoryAware, InitializingBean {
  3.     @Override
  4.         protected ModelAndView handleInternal(HttpServletRequest request,
  5.                         HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
  6.                 ModelAndView mav;
  7.                 checkRequest(request);
  8.                 if (this.synchronizeOnSession) {
  9.                         HttpSession session = request.getSession(false);
  10.                         if (session != null) {
  11.                                 Object mutex = WebUtils.getSessionMutex(session);
  12.                                 synchronized (mutex) {
  13.                                         // 执行请求
  14.                                         mav = invokeHandlerMethod(request, response, handlerMethod);
  15.                                 }
  16.                         }
  17.                         else {
  18.                                 // 执行请求
  19.                                 mav = invokeHandlerMethod(request, response, handlerMethod);
  20.                         }
  21.                 }
  22.                 else {
  23.                         // 执行请求
  24.                         mav = invokeHandlerMethod(request, response, handlerMethod);
  25.                 }
  26.                 // ...
  27.                 return mav;
  28.         }
  29.         @Nullable
  30.         protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
  31.                         HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
  32.                 ServletWebRequest webRequest = new ServletWebRequest(request, response);
  33.                 try {
  34.                         // ...
  35.             // 创建ServletInvocableHandlerMethod
  36.             ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
  37.                         // 调用invokeAndHandle方法处理请求
  38.                         invocableMethod.invokeAndHandle(webRequest, mavContainer);
  39.                         if (asyncManager.isConcurrentHandlingStarted()) {
  40.                                 return null;
  41.                         }
  42.                         // 返回ModelAndView
  43.                         return getModelAndView(mavContainer, modelFactory, webRequest);
  44.                 }
  45.                 finally {
  46.                         webRequest.requestCompleted();
  47.                 }
  48.         }
  49. }
复制代码
ServletInvocableHandlerMethod的invokeAndHandle中调用了invokeForRequest方法执行请求,它的实现在其父类InvocableHandlerMethod中:
  1. public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
  2.     public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
  3.                         Object... providedArgs) throws Exception {
  4.             // 执行请求
  5.                 Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
  6.                 setResponseStatus(webRequest);
  7.                 // ...
  8.         }
  9. }
复制代码
invokeForRequest中又调用了getMethodArgumentValues方法获取请求中的参数,处理逻辑如下:

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

  • 对获取到方法中的所有参数进行遍历,通过处理器调用resolveArgument方法解析请求中的数据,解析每一个参数对应的值;
  1. public class InvocableHandlerMethod extends HandlerMethod {
  2.         @Nullable
  3.         public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
  4.                         Object... providedArgs) throws Exception {
  5.                 // 获取请求中的参数
  6.                 Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
  7.                 if (logger.isTraceEnabled()) {
  8.                         logger.trace("Arguments: " + Arrays.toString(args));
  9.                 }
  10.                 return doInvoke(args);
  11.         }
  12.    
  13.     protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
  14.                         Object... providedArgs) throws Exception {
  15.                 // 获取方法的所有参数
  16.                 MethodParameter[] parameters = getMethodParameters();
  17.                 if (ObjectUtils.isEmpty(parameters)) {
  18.                         return EMPTY_ARGS;
  19.                 }
  20.                 Object[] args = new Object[parameters.length];
  21.                 // 对方法中的所有参数进行遍历
  22.                 for (int i = 0; i < parameters.length; i++) {
  23.                         MethodParameter parameter = parameters[i];
  24.                         parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
  25.                         // ...
  26.                         try {
  27.                                 // 调用resolveArgument从请求中解析对应的数据
  28.                                 args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
  29.                         }
  30.                         // ...
  31.                 }
  32.                 return args;
  33.         }
  34. }
复制代码
resolveArgument方法在HandlerMethodArgumentResolverComposite中实现:

  • 调用getArgumentResolver方法获取对应的参数处理器resolver;
  • 调用resolver的resolveArgument方法进行参数解析;
  1. public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
  2.         @Override
  3.         @Nullable
  4.         public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
  5.                         NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
  6.         // 获取对应的参数处理器
  7.                 HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
  8.                 if (resolver == null) {
  9.                         throw new IllegalArgumentException("Unsupported parameter type [" +
  10.                                         parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
  11.                 }
  12.                 // 解析参数
  13.                 return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
  14.         }
  15. }
复制代码
从断点中可以看到此时的resolver是RequestResponseBodyMethodProcessor类型的:

进入到RequestResponseBodyMethodProcessor的resolveArgument方法中,它又调用了readWithMessageConverters方法解析参数,最终会进入到
AbstractMessageConverterMethodArgumentResolve中的readWithMessageConverters方法:
  1. public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
  2.     @Override
  3.         public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
  4.                         NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
  5.                 parameter = parameter.nestedIfOptional();
  6.                 // 通过转换器进行参数解析
  7.                 Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
  8.                 String name = Conventions.getVariableNameForParameter(parameter);
  9.                 // ...
  10.                 return adaptArgumentIfNecessary(arg, parameter);
  11.         }
  12.         @Override
  13.         protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
  14.                         Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
  15.                 HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
  16.                 Assert.state(servletRequest != null, "No HttpServletRequest");
  17.                 ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
  18.                 // 调用AbstractMessageConverterMethodArgumentResolver中readWithMessageConverters方法读取参数
  19.                 Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
  20.                 if (arg == null && checkRequired(parameter)) {
  21.                         throw new HttpMessageNotReadableException("Required request body is missing: " +
  22.                                         parameter.getExecutable().toGenericString(), inputMessage);
  23.                 }
  24.                 return arg;
  25.         }
  26. }
复制代码
readWithMessageConverters方法处理逻辑如下:

  • 遍历所有HTTP消息转换器,判断是否支持解析当前的请求参数类型;
  • 如果转换器支持解析当前的参数类型并且有消息体内容,调用转换器的read方法进行解析;
  1. public abstract class AbstractMessageConverterMethodArgumentResolver implements HandlerMethodArgumentResolver {
  2.     @Nullable
  3.         protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
  4.                         Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
  5.                 // ...
  6.                 try {
  7.                         message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
  8.                         // 遍历所有的消息转换器
  9.                         for (HttpMessageConverter<?> converter : this.messageConverters) {
  10.                                 Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
  11.                                 GenericHttpMessageConverter<?> genericConverter =
  12.                                                 (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
  13.                                 // 判断是否支持当前参数类型的读取
  14.                                 if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
  15.                                                 (targetClass != null && converter.canRead(targetClass, contentType))) {
  16.                                         // 如果有消息体
  17.                                         if (message.hasBody()) {
  18.                                                 HttpInputMessage msgToUse =
  19.                                                                 getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
  20.                                                 // 调用read方法进行读取
  21.                                                 body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
  22.                                                                 ((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
  23.                                                 body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
  24.                                         }
  25.                                         else {
  26.                                                 body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
  27.                                         }
  28.                                         break;
  29.                                 }
  30.                         }
  31.                 }
  32.                 catch (IOException ex) {
  33.                         throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);
  34.                 }
  35.                 // ...
  36.                 return body;
  37.         }
  38. }
复制代码
这里列举一些消息转换器的类型:

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

  • 获取参数的Class类型,从断点中可以看出是[class com.example.demo.model.UserInfo];

  • 调用readJavaType方法解析参数
    (1)获取ContentType,前面可以看到请求接收的类型为application/json;
    (2)获取字符集,这里的字符集为UTF-8;
    (3)创建ObjectMapper对象,并从请求体中读取JSON数据,转为JAVA对象;
  1. public abstract class AbstractJackson2HttpMessageConverter extends AbstractGenericHttpMessageConverter<Object> {
  2.     @Override
  3.         public Object read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)
  4.                         throws IOException, HttpMessageNotReadableException {
  5.                 // 获取参数的Class类型
  6.                 JavaType javaType = getJavaType(type, contextClass);
  7.                 // 解析参数
  8.                 return readJavaType(javaType, inputMessage);
  9.         }
  10.         private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) throws IOException {
  11.                 // 获取ContentType
  12.                 MediaType contentType = inputMessage.getHeaders().getContentType();
  13.                 // 获取字符集
  14.                 Charset charset = getCharset(contentType);
  15.                 ObjectMapper objectMapper = selectObjectMapper(javaType.getRawClass(), contentType);
  16.                 Assert.state(objectMapper != null, "No ObjectMapper for " + javaType);
  17.                 boolean isUnicode = ENCODINGS.containsKey(charset.name());
  18.                 try {
  19.                     // ...
  20.                         if (isUnicode) {
  21.                                 // 获取HTTP请求体中的JSON数据,转为JAVA对象
  22.                                 return objectMapper.readValue(inputMessage.getBody(), javaType);
  23.                         }
  24.                         else {
  25.                                 Reader reader = new InputStreamReader(inputMessage.getBody(), charset);
  26.                                 return objectMapper.readValue(reader, javaType);
  27.                         }
  28.                 }
  29.                 // ....
  30.         }
  31. }
复制代码
到这里已经成功从HTTP请求体中的JSON数据,并转为JAVA对象,完成了参数的设置。
Spring版本:5.3.4

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

农妇山泉一亩田

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表