0
点赞
收藏
分享

微信扫一扫

Spring MVC 请求参数解析

上一篇记录了处理请求的过程,这篇记录对请求参数与返回参数的处理
在前面分析《Spring MVC 源码笔记 @EnableWebMvc》中,提到了完全配置类WebMvcConfigurationSupport,正是在这里声明的beanRequestMappingHandlerAdapter,创建了默认的消息转换器和方法参数解析器。

WebMvcConfigurationSupport
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
adapter.setContentNegotiationManager(mvcContentNegotiationManager());
adapter.setMessageConverters(getMessageConverters());
adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer());
adapter.setCustomArgumentResolvers(getArgumentResolvers());
adapter.setCustomReturnValueHandlers(getReturnValueHandlers());

if (jackson2Present) {
adapter.setRequestBodyAdvice(
Collections.<RequestBodyAdvice>singletonList(new JsonViewRequestBodyAdvice()));
adapter.setResponseBodyAdvice(
Collections.<ResponseBodyAdvice<?>>singletonList(new JsonViewResponseBodyAdvice()));
}

AsyncSupportConfigurer configurer = new AsyncSupportConfigurer();
configureAsyncSupport(configurer);
if (configurer.getTaskExecutor() != null) {
adapter.setTaskExecutor(configurer.getTaskExecutor());
}
if (configurer.getTimeout() != null) {
adapter.setAsyncRequestTimeout(configurer.getTimeout());
}
adapter.setCallableInterceptors(configurer.getCallableInterceptors());
adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors());

return adapter;
}

先看这里的getMessageConverters()

WebMvcConfigurationSupport
protected final List<HttpMessageConverter<?>> getMessageConverters() {
if (this.messageConverters == null) {
this.messageConverters = new ArrayList<HttpMessageConverter<?>>();
configureMessageConverters(this.messageConverters);
if (this.messageConverters.isEmpty()) {
addDefaultHttpMessageConverters(this.messageConverters);
}
extendMessageConverters(this.messageConverters);
}
return this.messageConverters;
}
...
protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();
stringConverter.setWriteAcceptCharset(false);

messageConverters.add(new ByteArrayHttpMessageConverter());
messageConverters.add(stringConverter);
messageConverters.add(new ResourceHttpMessageConverter());
messageConverters.add(new SourceHttpMessageConverter<Source>());
messageConverters.add(new AllEncompassingFormHttpMessageConverter());

if (romePresent) {
messageConverters.add(new AtomFeedHttpMessageConverter());
messageConverters.add(new RssChannelHttpMessageConverter());
}

if (jackson2XmlPresent) {
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.xml().applicationContext(this.applicationContext).build();
messageConverters.add(new MappingJackson2XmlHttpMessageConverter(objectMapper));
}
else if (jaxb2Present) {
messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
}

if (jackson2Present) {
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().applicationContext(this.applicationContext).build();
messageConverters.add(new MappingJackson2HttpMessageConverter(objectMapper));
}
else if (gsonPresent) {
messageConverters.add(new GsonHttpMessageConverter());
}
}

可以看到,addDefaultHttpMessageConverters内添加了默认的消息转换器。
RequestMappingHandlerAdapter也实现了接口InitializingBean,所以关注其afterPropertiesSet方法

RequestMappingHandlerAdapter
@Override
public void afterPropertiesSet() {
// Do this first, it may add ResponseBody advice beans
initControllerAdviceCache();

if (this.argumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.initBinderArgumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.returnValueHandlers == null) {
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}
...
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();

// Annotation-based argument resolution
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
resolvers.add(new RequestParamMapMethodArgumentResolver());
resolvers.add(new PathVariableMethodArgumentResolver());
resolvers.add(new PathVariableMapMethodArgumentResolver());
resolvers.add(new MatrixVariableMethodArgumentResolver());
resolvers.add(new MatrixVariableMapMethodArgumentResolver());
resolvers.add(new ServletModelAttributeMethodProcessor(false));
resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
resolvers.add(new RequestHeaderMapMethodArgumentResolver());
resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new SessionAttributeMethodArgumentResolver());
resolvers.add(new RequestAttributeMethodArgumentResolver());

// Type-based argument resolution
resolvers.add(new ServletRequestMethodArgumentResolver());
resolvers.add(new ServletResponseMethodArgumentResolver());
resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RedirectAttributesMethodArgumentResolver());
resolvers.add(new ModelMethodProcessor());
resolvers.add(new MapMethodProcessor());
resolvers.add(new ErrorsMethodArgumentResolver());
resolvers.add(new SessionStatusMethodArgumentResolver());
resolvers.add(new UriComponentsBuilderMethodArgumentResolver());

// Custom arguments
if (getCustomArgumentResolvers() != null) {
resolvers.addAll(getCustomArgumentResolvers());
}

// Catch-all
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
resolvers.add(new ServletModelAttributeMethodProcessor(true));

return resolvers;
}

可以看到,在getDefaultArgumentResolvers方法内,创建了多个方法参数解析器,(其中RequestParamMethodArgumentResolver添加了两次,不同之处为最后添加的为默认的参数解析器)最后在HandlerMethodArgumentResolverCompositeaddResolvers中将创建的方法参数解析器保存在其argumentResolvers变量上。

  • 记录完方法参数解析器的初始化,直接来到执行方法的片段。
    方法的调用在InvocableHandlerMethodinvokeForRequest
InvocableHandlerMethod
public Object invokeForRequest(NativeWebRequest request, 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方法用来获取请求参数

InvocableHandlerMethod
private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {

MethodParameter[] parameters = getMethodParameters();
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;
}
if (this.argumentResolvers.supportsParameter(parameter)) {
try {
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.getMethod().toGenericString() +
": " + getArgumentResolutionErrorMessage("No suitable resolver for", i));
}
}
return args;
}

getMethodParameters方法是InvocableHandlerMethodHandlerMethod继承而来

HandlerMethod
public MethodParameter[] getMethodParameters() {
return this.parameters;
}

而关于parameters的初始化是在RequestMappingHandlerMappingafterPropertiesSet方法执行时调用,只是简单的封装方法参数,不需要太过关注。parameters数组的长度为方法的参数个数。
this.argumentResolversInvocableHandlerMethod默认创建的HandlerMethodArgumentResolverComposite,关注其supportsParameter方法

HandlerMethodArgumentResolverComposite
@Override
public boolean supportsParameter(MethodParameter parameter) {
return (getArgumentResolver(parameter) != null);
}
...
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
if (result == null) {
for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {
if (logger.isTraceEnabled()) {
logger.trace("Testing if argument resolver [" + methodArgumentResolver + "] supports [" +
parameter.getGenericParameterType() + "]");
}
if (methodArgumentResolver.supportsParameter(parameter)) {
result = methodArgumentResolver;
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}

这里的this.argumentResolvers为内置的方法参数解析器,最开始已经说明。展示几个常用的方法参数解析器

RequestParamMethodArgumentResolver
@Override
public boolean supportsParameter(MethodParameter parameter) {
if (parameter.hasParameterAnnotation(RequestParam.class)) {
if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
String paramName = parameter.getParameterAnnotation(RequestParam.class).name();
return StringUtils.hasText(paramName);
}
else {
return true;
}
}
else {
if (parameter.hasParameterAnnotation(RequestPart.class)) {
return false;
}
parameter = parameter.nestedIfOptional();
if (MultipartResolutionDelegate.isMultipartArgument(parameter)) {
return true;
}
else if (this.useDefaultResolution) {
return BeanUtils.isSimpleProperty(parameter.getNestedParameterType());
}
else {
return false;
}
}
}

第一个是RequestParamMethodArgumentResolver,可以看到,如果controller的method的参数列表有使用@RequestParam注解,将会使用RequestParamMethodArgumentResolver来解析参数。如果找不到合适的处理参数的解析器,也是用此解析器来处理,因为在内置的方法参数解析器中有一个RequestParamMethodArgumentResolverthis.useDefaultResolution为true,表示默认。

RequestResponseBodyMethodProcessor
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestBody.class);
}

第二个是RequestResponseBodyMethodProcessor,可以看到是判断方法上有没有@RequestBody注解。
回到InvocableHandlerMethodgetMethodArgumentValues方法中,如果参数解析HandlerMethodArgumentResolverComposite内的方法参数解析器能解析当前参数,会调用其resolveArgument方法

HandlerMethodArgumentResolverComposite
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
throws Exception {

HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
if (resolver == null) {
throw new IllegalArgumentException("Unknown parameter type [" + parameter.getParameterType().getName() + "]");
}
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}

可以看到实际调用了选中的方法参数解析器的resolveArgument方法。这里选用RequestResponseBodyMethodProcessor来看下

`RequestResponseBodyMethodProcessor`
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
throws Exception {

parameter = parameter.nestedIfOptional();
Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
String name = Conventions.getVariableNameForParameter(parameter);

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());
}
}
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());

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);
ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);

Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
if (arg == null) {
if (checkRequired(parameter)) {
throw new HttpMessageNotReadableException("Required request body is missing: " +
parameter.getMethod().toGenericString());
}
}
return arg;
}
...

关注readWithMessageConverters方法,此方法在其基类AbstractMessageConverterMethodArgumentResolver

AbstractMessageConverterMethodArgumentResolver
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType)
throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

MediaType contentType;
boolean noContentType = false;
try {
contentType = inputMessage.getHeaders().getContentType();
}
catch (InvalidMediaTypeException ex) {
throw new HttpMediaTypeNotSupportedException(ex.getMessage());
}
if (contentType == null) {
noContentType = true;
contentType = MediaType.APPLICATION_OCTET_STREAM;
}

Class<?> contextClass = (parameter != null ? parameter.getContainingClass() : null);
Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
if (targetClass == null) {
ResolvableType resolvableType = (parameter != null ?
ResolvableType.forMethodParameter(parameter) : ResolvableType.forType(targetType));
targetClass = (Class<T>) resolvableType.resolve();
}

HttpMethod httpMethod = ((HttpRequest) inputMessage).getMethod();
Object body = NO_VALUE;

try {
inputMessage = new EmptyBodyCheckingHttpInputMessage(inputMessage);

for (HttpMessageConverter<?> converter : this.messageConverters) {
Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
if (converter instanceof GenericHttpMessageConverter) {
GenericHttpMessageConverter<?> genericConverter = (GenericHttpMessageConverter<?>) converter;
if (genericConverter.canRead(targetType, contextClass, contentType)) {
if (logger.isDebugEnabled()) {
logger.debug("Read [" + targetType + "] as \"" + contentType + "\" with [" + converter + "]");
}
if (inputMessage.getBody() != null) {
inputMessage = getAdvice().beforeBodyRead(inputMessage, parameter, targetType, converterType);
body = genericConverter.read(targetType, contextClass, inputMessage);
body = getAdvice().afterBodyRead(body, inputMessage, parameter, targetType, converterType);
}
else {
body = getAdvice().handleEmptyBody(null, inputMessage, parameter, targetType, converterType);
}
break;
}
}
else if (targetClass != null) {
if (converter.canRead(targetClass, contentType)) {
if (logger.isDebugEnabled()) {
logger.debug("Read [" + targetType + "] as \"" + contentType + "\" with [" + converter + "]");
}
if (inputMessage.getBody() != null) {
inputMessage = getAdvice().beforeBodyRead(inputMessage, parameter, targetType, converterType);
body = ((HttpMessageConverter<T>) converter).read(targetClass, inputMessage);
body = getAdvice().afterBodyRead(body, inputMessage, parameter, targetType, converterType);
}
else {
body = getAdvice().handleEmptyBody(null, inputMessage, parameter, targetType, converterType);
}
break;
}
}
}
}
catch (IOException ex) {
throw new HttpMessageNotReadableException("Could not read document: " + ex.getMessage(), ex);
}

if (body == NO_VALUE) {
if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) ||
(noContentType && inputMessage.getBody() == null)) {
return null;
}
throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
}

return body;
}

既然选择了RequestResponseBodyMethodProcessor@RequestBody注解,那么传入的参数一定为json数据。for循环遍历的消息转换器this.messageConverters在开始已经记录过,当遍历到GsonHttpMessageConverter时,它是GenericHttpMessageConverter的间接子类,通过instanceof判断后,关注GsonHttpMessageConverter的父类AbstractGenericHttpMessageConvertercanRead方法

AbstractGenericHttpMessageConverter
@Override
public boolean canRead(Type type, Class<?> contextClass, MediaType mediaType) {
return (type instanceof Class ? canRead((Class<?>) type, mediaType) : canRead(mediaType));
}

再到父类AbstractHttpMessageConvertercanRead

AbstractHttpMessageConverter
@Override
public boolean canRead(Class<?> clazz, MediaType mediaType) {
return supports(clazz) && canRead(mediaType);
}
...
protected boolean canRead(MediaType mediaType) {
if (mediaType == null) {
return true;
}
for (MediaType supportedMediaType : getSupportedMediaTypes()) {
if (supportedMediaType.includes(mediaType)) {
return true;
}
}
return false;
}

传入的mediaType为发起请求时指定的'Content-TypegetSupportedMediaTypes返回的是当前消息转换器支持的MediaType,对于GsonHttpMessageConverter来说,它是支持此Content-Type的,这在它的构造器中很容易看到。

GsonHttpMessageConverter
public GsonHttpMessageConverter() {
super(MediaType.APPLICATION_JSON, new MediaType("application", "*+json"));
this.setDefaultCharset(DEFAULT_CHARSET);
}

选中消息转换器后,开始执行其read方法

GsonHttpMessageConverter
@Override
@SuppressWarnings("deprecation")
public Object read(Type type, Class<?> contextClass, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {

TypeToken<?> token = getTypeToken(type);
return readTypeToken(token, inputMessage);
}
...
private Object readTypeToken(TypeToken<?> token, HttpInputMessage inputMessage) throws IOException {
Reader json = new InputStreamReader(inputMessage.getBody(), getCharset(inputMessage.getHeaders()));
try {
return this.gson.fromJson(json, token.getType());
}
catch (JsonParseException ex) {
throw new HttpMessageNotReadableException("Could not read JSON document: " + ex.getMessage(), ex);
}
}

type为当前处理请求的方法的参数类型,TypeToken是谷歌json包中的类,用来封装参数类型。在readTypeToken方法中,可以看到使用this.gson.fromJson将HttpInputMessage转化的流转化为java类的过程。(这里就是对controller请求的json格式的参数,是怎么被SpringMVC封装到我们指定的model里的过程)
重新回到AbstractMessageConverterMethodArgumentResolverreadWithMessageConverters方法,可以看到在消息转换器执行read动作前后,执行了RequestResponseBodyAdviceChain的环绕方法。
回到RequestResponseBodyMethodProcessorresolveArgument方法,再回到InvocableHandlerMethodgetMethodArgumentValues方法中,方法参数解析完成。

总结:

  • WebMvcConfigurationSupport配置的RequestMappingHandlerAdapter负责初始化消息转换器和方法参数解析器
  • HandlerMethodArgumentResolverComposite内遍历方法参数解析器以选中能处理当前对应的方法的参数解析器。
  • 这里挑选了处理json格式参数的解析器RequestResponseBodyMethodProcessor
  • RequestResponseBodyMethodProcessor的间接父类AbstractMessageConverterMethodArgumentResolver中选出了处理json格式参数的消息转换器GsonHttpMessageConverter
  • 在消息转换器GsonHttpMessageConverter中使用谷歌的gson(默认jackson,这里因为引了gson的jar所以被替代)将HttpInputMessage转化的流转化为处理请求的方法参数中指定的java类并返回。
举报

相关推荐

0 条评论