@RequestBody註解可以用於POST請求接收請求體中的參數,使用方式如下: ``` java @Controller public class IndexController { @PostMapping(value = "/submit", produces = MediaType.APP ...
@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
方法中:
- 調用
getHandler
方法獲取請求對應的Handler處理器; - 根據
handler
獲取對應的適配器,這裡用到了適配器模式; - 調用適配器的
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
:
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
方法進行處理:
- 創建
ServletInvocableHandlerMethod
; - 調用invokeAndHandle方法繼續請求處理;
- 調用
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();
}
}
}
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
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
方法處理邏輯如下:
-
遍歷所有HTTP消息轉換器,判斷是否支持解析當前的請求參數類型;
-
如果轉換器支持解析當前的參數類型並且有消息體內容,調用轉換器的
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;
}
}
這裡列舉一些消息轉換器的類型:
對於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<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