【Spring】@RequestBody的实现原理

@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的核心,它对请求进行调度,收到请求后会进入DispatcherServletdoDispatch方法中:

  1. 调用getHandler方法获取请求对应的Handler处理器;
  2. 根据handler获取对应的适配器,这里用到了适配器模式;
  3. 调用适配器的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:
【Spring】@RequestBody的实现原理

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; } 

所以回到RequestMappingHandlerAdapterhandleInternal方法,里面调用了invokeHandlerMethod方法进行处理:

  1. 创建ServletInvocableHandlerMethod
  2. 调用invokeAndHandle方法继续请求处理;
  3. 调用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(); 		} 	} } 

ServletInvocableHandlerMethodinvokeAndHandle中调用了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方法获取请求中的参数,处理逻辑如下:

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

  2. 对获取到方法中的所有参数进行遍历,通过处理器调用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中实现:

  1. 调用getArgumentResolver方法获取对应的参数处理器resolver

  2. 调用resolverresolveArgument方法进行参数解析;

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

从断点中可以看到此时的resolverRequestResponseBodyMethodProcessor类型的:
【Spring】@RequestBody的实现原理

进入到RequestResponseBodyMethodProcessorresolveArgument方法中,它又调用了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方法处理逻辑如下:

  1. 遍历所有HTTP消息转换器,判断是否支持解析当前的请求参数类型;

  2. 如果转换器支持解析当前的参数类型并且有消息体内容,调用转换器的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; 	} } 

这里列举一些消息转换器的类型:
【Spring】@RequestBody的实现原理

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

  1. 获取参数的Class类型,从断点中可以看出是[class com.example.demo.model.UserInfo];
    【Spring】@RequestBody的实现原理

  2. 调用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

发表评论

评论已关闭。

相关文章