交流
商城
MCN
登入
注册
首页
提问
分享
讨论
建议
公告
动态
发表新帖
发表新帖
springmvc第4-demo-5:@ResponseBody RequestResponseBodyMethodProcessor
分享
未结
0
970
李延
LV6
2021-06-05
悬赏:20积分
# 作用 解析@RequestBody的参数,并且处理标注了@ResponseBody的防治 # 继承关系  这里我们看到不但继承了HandlerMethodArgumentResolver 处理参数,也继承了HandlerMethodReturnValueHandler处理返回值 # HandlerMethodArgumentResolver 实现 ## supportsParameter ```java @Override public boolean supportsParameter(MethodParameter parameter) { return parameter.hasParameterAnnotation(RequestBody.class); } ``` 我们看到这里直接处理所有带RequestBody的参数 ## resolveArgument ```java @Override public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { //去除Optional 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) { //参数验证,处理Validated注解 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()); } } // 类型转换,主要是查看是否为Optional对象 return adaptArgumentIfNecessary(arg, parameter); } ``` 这里主要有2个方法:一个为readWithMessageConverters。一个为validateIfApplicable。我们先看readWithMessageConverters参数是如何绑定的 ```java @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); 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; } ``` 我们看到这里分装了inputMessage对象。通过readWithMessageConverters方法处理参数。 ```java @Nullable protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException { MediaType contentType; boolean noContentType = false; //获取contentType 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.getContainingClass(); //获取需要解析的参数类型 Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null); if (targetClass == null) { ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter); targetClass = (Class<T>) resolvableType.resolve(); } //获取请求类型 HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null); Object body = NO_VALUE; EmptyBodyCheckingHttpInputMessage message; try { message = new EmptyBodyCheckingHttpInputMessage(inputMessage); //循环所以的HttpMessageConverter对象 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); 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); } if (body == NO_VALUE) { if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) || (noContentType && !message.hasBody())) { return null; } throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes); } MediaType selectedContentType = contentType; Object theBody = body; LogFormatUtils.traceDebug(logger, traceOn -> { String formatted = LogFormatUtils.formatValue(theBody, !traceOn); return "Read \"" + selectedContentType + "\" to [" + formatted + "]"; }); return body; } ``` 我们看见所有的处理过程都是交给HttpMessageConverter对象来完成,而在处理前后又通过RequestResponseBodyAdviceChain对象进行特殊处理。 下一步我们看一下这两个对象是在哪里创建的,并且又起到什么作用 ## HttpMessageConverter 在构造方法中我们看到: ```java public RequestPartMethodArgumentResolver(List<HttpMessageConverter<?>> messageConverters) { super(messageConverters); } ``` 在这里注入了HttpMessageConverter对象。我们再回到RequestMappingHandlerAdapter。看一下RequestPartMethodArgumentResolver对象创建时的地方  这里我们看到通过RequestMappingHandlerAdapter的getMessageConverters()方法获取对象 最后我们在WebMvcConfigurationSupport的getMessageConverters()中找到了它的初始化  ### MappingJackson2HttpMessageConverter 对于我们json传参,通过MappingJackson2HttpMessageConverter来处理,我们主要关注这个类 #### canRead ```java @Override public boolean canRead(Class<?> clazz, @Nullable MediaType mediaType) { return canRead(clazz, null, mediaType); } @Override public boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType) { if (!canRead(mediaType)) { return false; } JavaType javaType = getJavaType(type, contextClass); AtomicReference<Throwable> causeRef = new AtomicReference<>(); if (this.objectMapper.canDeserialize(javaType, causeRef)) { return true; } logWarningIfNecessary(javaType, causeRef.get()); return false; } protected boolean canRead(@Nullable MediaType mediaType) { if (mediaType == null) { return true; } for (MediaType supportedMediaType : getSupportedMediaTypes()) { if (supportedMediaType.includes(mediaType)) { return true; } } return false; } ``` 这里我们看到,只处理没有content-type,和content-type为接送的请求 #### read ```java @Override public final T read(Class<? extends T> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { return readInternal(clazz, inputMessage); } @Override protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { JavaType javaType = getJavaType(clazz, null); return readJavaType(javaType, inputMessage); } private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) throws IOException { MediaType contentType = inputMessage.getHeaders().getContentType(); Charset charset = getCharset(contentType); boolean isUnicode = ENCODINGS.containsKey(charset.name()); try { if (inputMessage instanceof MappingJacksonInputMessage) { Class<?> deserializationView = ((MappingJacksonInputMessage) inputMessage).getDeserializationView(); if (deserializationView != null) { ObjectReader objectReader = this.objectMapper.readerWithView(deserializationView).forType(javaType); if (isUnicode) { return objectReader.readValue(inputMessage.getBody()); } else { Reader reader = new InputStreamReader(inputMessage.getBody(), charset); return objectReader.readValue(reader); } } } if (isUnicode) { return this.objectMapper.readValue(inputMessage.getBody(), javaType); } else { Reader reader = new InputStreamReader(inputMessage.getBody(), charset); return this.objectMapper.readValue(reader, javaType); } } catch (InvalidDefinitionException ex) { throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex); } catch (JsonProcessingException ex) { throw new HttpMessageNotReadableException("JSON parse error: " + ex.getOriginalMessage(), ex, inputMessage); } } ``` 这里就不详细说明了,主要就是 通过objectMapper转化为我们需要的对象。 ### JsonViewRequestBodyAdvice 我们在出去参数前后看到有调用advice的方法。而这些都是处理JsonView注解的过程。 # HandlerMethodReturnValueHandler 实现 当我们需要返回json数据时,会使用此类 ## supportsReturnType ```java @Override public boolean supportsReturnType(MethodParameter returnType) { return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) || returnType.hasMethodAnnotation(ResponseBody.class)); } ``` 这里主要有2个判断逻辑: - 返回值上有ResponseBody注解,或者方法上有ResponseBody注解。 我们回头再看一下我们常用的RestController注解。  它的上面就有ResponseBody ## handleReturnValue 对于返回值的处理 其实和接受参数比较相似,也是通过HttpMessageConverter来处理的,这里就不再重复
回帖
消灭零回复
提交回复
热议榜
java 相关知识分享
8
好的程序员与不好的程序员
6
写给工程师的十条精进原则
5
spring boot以jar包运行配置的logback日志文件没生成
5
一步一步分析SpringBoot启动源码(一)
5
MockMvc测试
5
【吐槽向】是不是有个吐槽的板块比较好玩
4
logstash jdbc同步mysql多表数据到elasticsearch
3
IntelliJ IDEA 优质License Server
3
.gitignore忽略规则
3
SpringBoot启动源码分析
3
一步一步分析SpringBoot启动源码(三)
3
2
一步一步分析SpringBoot启动源码(二)
2
积分不够将无法发表新帖
2
官方产品
Meta-Boot - 基于MCN
MCN - 快速构建SpringBoot应用
微信扫码关注公众号