1. @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. Optionally, automatic
* validation can be applied by annotating the argument with {@code @Valid}.
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestBody {
/**
* Whether body content is required.
* <p>Default is {@code true}, leading to an exception thrown in case
* there is no body content. Switch this to {@code false} if you prefer
* {@code null} to be passed when the body content is {@code null}.
* @since 3.2
*/
boolean required() default true;
}
從源碼中可以看到洒擦,@RequestBody 用在方法參數(shù)上面,用來將請求參數(shù)綁定到request body中黔攒,通過HttpMessageConverter
封裝為具體的JavaBean蘸拔。通俗點講就是你在一個參數(shù)上加上該注解淤毛,spring就會將request body中的json/xml對象解析成該參數(shù)類型的Javabean對象蒜胖。
作為RESTful開發(fā)中經(jīng)常用到的注解消别,研究其原理有利于我們更好地理解并掌握它。
那么spring是如何做到這一點的呢台谢?先來看DispatcherServlet寻狂。
作為springMVC處理請求的中央調(diào)度器,DispatcherServlet本身是一個servlet对碌,所以我們看doService():
/**
* Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
* for the actual dispatching.
*/
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
" processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
}
//......
//......
try {
doDispatch(request, response);
}
finally {
//......
}
}
重點在doDispatch()方法,該方法先找到會找到合適的handler來處理當(dāng)前請求:
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
HandlerAdapter是一個接口荆虱,具體處理方法在RequestMappingHandlerAdapter
類中:
用
@RequestMapping
(包括@GetMapping
、@PostMapping
等)注釋修飾的接口請求會用該類處理朽们。RequestMappingHandlerAdapter實現(xiàn)了AbstractHandlerMethodAdapter,所以mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
實際上調(diào)用的是AbstractHandlerMethodAdapter類的handle()方法:
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
這里又調(diào)用了handleInternal()方法,RequestMappingHandlerAdapter重寫了該方法:
進入該方法诉位,
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 necessary
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No synchronization on session demanded at all...
mav = invokeHandlerMethod(request, response, handlerMethod);
}
可以看到最終調(diào)用的都是invokeHandlerMethod()方法,此方法會處理@RequestMapping修飾的請求
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
//注意,此處往ServletInvocableHandlerMethod中設(shè)置了一系列的參數(shù)解析器
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
if (logger.isDebugEnabled()) {
logger.debug("Found concurrent result value [" + result + "]");
}
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
//具體請求處理方法
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
進入該方法的invocableMethod.invokeAndHandle(webRequest, mavContainer);
骑脱,來到ServletInvocableHandlerMethod,此類繼承了InvocableHandlerMethod,可以處理請求的返回值苍糠。invokeAndHandle()方法:
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
}
throw ex;
}
}
重點在Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
,通過請求調(diào)用并產(chǎn)生返回值叁丧。
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
"' with arguments " + Arrays.toString(args));
}
Object returnValue = doInvoke(args);
if (logger.isTraceEnabled()) {
logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
"] returned [" + returnValue + "]");
}
return returnValue;
}
getMethodArgumentValues()方法的作用是獲取方法參數(shù),重點就在這里,
private Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
MethodParameter[] parameters = getMethodParameters();
//創(chuàng)建一個Object數(shù)組用于存放解析后的參數(shù)列表
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] = resolveProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
//判斷當(dāng)前的參數(shù)解析器是否支持解析該參數(shù)
if (this.argumentResolvers.supportsParameter(parameter)) {
try {
//解析參數(shù)
args[i] = this.argumentResolvers.resolveArgument(
parameter, mavContainer, request, this.dataBinderFactory);
continue;
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex);
}
throw ex;
}
}
if (args[i] == null) {
throw new IllegalStateException("Could not resolve method parameter at index " +
parameter.getParameterIndex() + " in " + parameter.getExecutable().toGenericString() +
": " + getArgumentResolutionErrorMessage("No suitable resolver for", i));
}
}
return args;
}
進入resolveArgument()方法,
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
//找到對應(yīng)解析器
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
if (resolver == null) {
throw new IllegalArgumentException("Unknown parameter type [" + parameter.getParameterType().getName() + "]");
}
//解析參數(shù)
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
@RequestBody修飾的參數(shù)會使用RequestResponseBodyMethodProcessor解析拥娄,
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);
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
if (arg != null) {
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
}
}
if (mavContainer != null) {
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
}
}
return adaptArgumentIfNecessary(arg, parameter);
}
進入readWithMessageConverters()方法一路順藤摸瓜蚊锹,來到AbstractMessageConverterMethodArgumentResolver的readWithMessageConverters(),
Object body = NO_VALUE;
EmptyBodyCheckingHttpInputMessage message;
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 (logger.isDebugEnabled()) {
logger.debug("Read [" + targetType + "] as \"" + contentType + "\" with [" + converter + "]");
}
if (message.hasBody()) {
//解析HttpMessage
HttpInputMessage msgToUse =
getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
//使用相應(yīng)的轉(zhuǎn)換器
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);
}
可以看到使用文章開頭提到的HttpMessageConverter解析參數(shù)并返回,而此處的HttpMessageConverter是在RequestMappingHandlerAdapter中設(shè)置解析器的時候添加到每個解析器中的稚瘾。而json格式的數(shù)據(jù)使用AbstractJackson2HttpMessageConverter進行解析牡昆,內(nèi)部使用jackson進行json數(shù)據(jù)的解析。
總結(jié)
請求由DispatcherServlet處理摊欠,找到相應(yīng)的HandlerAdapter進行處理丢烘,RequestMappingHandlerAdapter會處理@RequestMapping注解的請求,設(shè)置一系列參數(shù)解析器進行解析些椒,如果參數(shù)使用@RequestBody注解播瞳,則使用RequestResponseBodyMethodProcessor進行解析,此參數(shù)解析器用HttpMessageConverter將HttpMessage封裝為具體的JavaBean對象免糕,json格式的數(shù)據(jù)使用AbstractJackson2HttpMessageConverter進行解析赢乓,內(nèi)部使用jackson進行json數(shù)據(jù)的解析。