@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的核心,它对请求进行调度,收到请求后会进入DispatcherServlet的doDispatch方法中:
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:
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; }
所以回到RequestMappingHandlerAdapter的handleInternal方法,里面调用了invokeHandlerMethod方法进行处理:
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(); } } }
ServletInvocableHandlerMethod的invokeAndHandle中调用了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方法获取请求中的参数,处理逻辑如下:
调用getMethodParameters获取方法中的参数,也就是我们的请求处理器方法中的所有参数,上面看到submit只接收了一个UserInfo类型的参数,这里可以从断点中看到parameters中只有一个元素,类型为UserInfo:
对获取到方法中的所有参数进行遍历,通过处理器调用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中实现:
调用getArgumentResolver方法获取对应的参数处理器resolver;
调用resolver的resolveArgument方法进行参数解析;
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); } }
从断点中可以看到此时的resolver是RequestResponseBodyMethodProcessor类型的:
进入到RequestResponseBodyMethodProcessor的resolveArgument方法中,它又调用了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 protectedObject 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方法处理逻辑如下:
遍历所有HTTP消息转换器,判断是否支持解析当前的请求参数类型;
如果转换器支持解析当前的参数类型并且有消息体内容,调用转换器的read方法进行解析;
public abstract class AbstractMessageConverterMethodArgumentResolver implements HandlerMethodArgumentResolver { @Nullable protectedObject readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException { // ... try { message = new EmptyBodyCheckingHttpInputMessage(inputMessage); // 遍历所有的消息转换器 for (HttpMessageConverter> converter : this.messageConverters) { Class > converterType = (Class >) 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 ) 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; } }
这里列举一些消息转换器的类型:
对于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对象;
public abstract class AbstractJackson2HttpMessageConverter extends AbstractGenericHttpMessageConverter
到这里已经成功从HTTP请求体中的JSON数据,并转为JAVA对象,完成了参数的设置。