RequestResponseBodyMethodProcessor类源码解析
- 参数解析器接口实现逻辑
- 返回值处理器接口解析逻辑
- 总结
这个类用于解析@RequestBody入参和@ResponseBody返回值解析问题。先查看一些类继承结构图:
如上图描述,以上几个类作用标记如图。标记几个类的有部分相同的代码逻辑,同时也只有以上几种情况下@ControllerAdvice注解的切面才会生效,其余情况不会的入参和返回值不会对其产生作用。
参数解析器接口实现逻辑
- HandlerMethodArgumentResolver接口的supportsParameter方法实现逻辑
//只支持参数使用@RequestBody注解
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestBody.class);
}
- HandlerMethodArgumentResolver接口的resolveArgument方法实现逻辑
@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注解或者使用了Vlid开头的注解,则使用Validator接口实现类校验数据(如果适用)
validateIfApplicable(binder, parameter);
//判断校验结果是否有错误,然后判断当前参数后挨着的是不是BindingResult对象
//如果不是则报错,可以通过全局异常处理的形式处理返回校验结果(推荐)
//如果不是,则由ErrorsMethodArgumentResolver参数解析器将校验结果复制到BindingResult入参对象中,可以在方法中处理或者配合切面处理
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);
}
- 使用转换器读取请求参数
//读取请求入参
@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);
//如果入参为空,校验@RequestBody的required属性是否为true
if (arg == null && checkRequired(parameter)) {
throw new HttpMessageNotReadableException("Required request body is missing: " +
parameter.getExecutable().toGenericString());
}
return arg;
}
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.getContainingClass();
//获取入参声明的类型
Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
if (targetClass == null) {
ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
targetClass = (Class<T>) resolvableType.resolve();
}
//获取请求方法(post get...)
HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);
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(".....");
}
//判断请求有请求头体
if (message.hasBody()) {
//循环调用ControllerAdvice切面RequestBodyAdvice接口所有实现的beforeBodyRead方法(会先判断是否切当前调用的类)
HttpInputMessage msgToUse = getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
//调用转换器的read方法,反序列化入参(具体由转换器类实现)
body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
//循环调用ControllerAdvice切面所有RequestBodyAdvice接口所有实现的afterBodyRead方法(会先判断是否切当前调用的类)
body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
}else {
//循环调用ControllerAdvice切面RequestBodyAdvice接口所有实现的handleEmptyBody方法(会先判断是否切当前调用的类)
body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
}
break;
}
}
}
catch (IOException ex) {
throw new HttpMessageNotReadableException("I/O error while reading input message", ex);
}
if (body == NO_VALUE) {
if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) ||
(noContentType && !message.hasBody())) {
return null;
}
throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
}
return body;
}
使用消息转换器读取消息的时候,是由HttpMessageConverter接口实现的,可以实现该接口自定义转换器。基本上使用内置的转换器就能满足需求了,后续我们介绍一下JSON处理最常用的转换器MappingJackson2HttpMessageConverter类。
- 获取数据板绑定器
//获取数据绑定器 DefaultDataBinderFactory#createBinder方法
public final WebDataBinder createBinder(
NativeWebRequest webRequest, @Nullable Object target, String objectName) throws Exception {
//创建一个DataBinder实例
WebDataBinder dataBinder = createBinderInstance(target, objectName, webRequest);
if (this.initializer != null) {
//初始化DataBinder实例
this.initializer.initBinder(dataBinder, webRequest);
}
//先调用@InitBinder注解标记的方法,进行前置复制处理
initBinder(dataBinder, webRequest);
return dataBinder;
}
//初始化DataBinder实例调用ConfigurableWebBindingInitializer#initBinder方法,设置使用的组件,有的是自定义的
@Override
public void initBinder(WebDataBinder binder) {
binder.setAutoGrowNestedPaths(this.autoGrowNestedPaths);
if (this.directFieldAccess) {
binder.initDirectFieldAccess();
}
if (this.messageCodesResolver != null) {
binder.setMessageCodesResolver(this.messageCodesResolver);
}
if (this.bindingErrorProcessor != null) {
binder.setBindingErrorProcessor(this.bindingErrorProcessor);
}
if (this.validator != null && binder.getTarget() != null &&
this.validator.supports(binder.getTarget().getClass())) {
binder.setValidator(this.validator);
}
if (this.conversionService != null) {
binder.setConversionService(this.conversionService);
}
if (this.propertyEditorRegistrars != null) {
for (PropertyEditorRegistrar propertyEditorRegistrar : this.propertyEditorRegistrars) {
propertyEditorRegistrar.registerCustomEditors(binder);
}
}
}
- 调用Validator接口实现进行参数校验
@Valid @NotBlank等注解就是在这一步生效的
//数据校验 AbstractMessageConverterMethodArgumentResolver#validateIfApplicable方法
protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
//获取参数上的所有注解
Annotation[] annotations = parameter.getParameterAnnotations();
for (Annotation ann : annotations) {
//获取@Validated注解(或者该注解的子类注解)
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
//获取到的注解不为空或者拿到的注解名是Valid开头则可以校验(@Valid注解生效原因)
if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
//获取注解中value属性的值
Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
binder.validate(validationHints);
break;
}
}
}
//调用校验方法DataBinder#validate
public void validate(Object... validationHints) {
for (Validator validator : getValidators()) {
//判断校验器的类型,调用校验的方法,将校验结果放到BindingResult对象中
if (!ObjectUtils.isEmpty(validationHints) && validator instanceof SmartValidator) {
((SmartValidator) validator).validate(getTarget(), getBindingResult(), validationHints);
}else if (validator != null) {
validator.validate(getTarget(), getBindingResult());
}
}
}
可以实现Validator接口使用自定义的校验器,但是校验器写一个完备的比较复杂,建议使用默认的校验实现类即可。可以通过实现ConstraintValidator接口扩展自定义的校验规则,自动会添加到默认实现类中。
返回值处理器接口解析逻辑
- 支持处理的条件supportsReturnType方法实现逻辑
//必须类上面或者方法上有@ResponseBody注解
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
returnType.hasMethodAnnotation(ResponseBody.class));
}
- 处理返回值的逻辑handleReturnValue方法
//构建读取组件和参数
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
mavContainer.setRequestHandled(true);
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
// Try even with null return value. ResponseBodyAdvice could get involved.
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
Object outputValue;
Class<?> valueType;
Type declaredType;
//处理字符数组接口类型,转换成字符串类型
if (value instanceof CharSequence) {
outputValue = value.toString();
valueType = String.class;
declaredType = String.class;
}else {
//其余类型
outputValue = value;
valueType = getReturnValueType(outputValue, returnType);
declaredType = getGenericType(returnType); //获取泛型
}
//判断是否是资源类型的返回值(InputStreamResource类或者Resource类)
if (isResourceType(value, returnType)) {
outputMessage.getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes");
if (value != null && inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null) {
Resource resource = (Resource) value;
try {
List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();
outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value());
outputValue = HttpRange.toResourceRegions(httpRanges, resource);
valueType = outputValue.getClass();
declaredType = RESOURCE_REGION_LIST_TYPE;
}
catch (IllegalArgumentException ex) {
outputMessage.getHeaders().set(HttpHeaders.CONTENT_RANGE, "bytes */" + resource.contentLength());
outputMessage.getServletResponse().setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value());
}
}
}
List<MediaType> mediaTypesToUse;
//获取媒体类型
MediaType contentType = outputMessage.getHeaders().getContentType();
if (contentType != null && contentType.isConcrete()) {
mediaTypesToUse = Collections.singletonList(contentType);
}
else {
HttpServletRequest request = inputMessage.getServletRequest();
List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(request);
List<MediaType> producibleMediaTypes = getProducibleMediaTypes(request, valueType, declaredType);
if (outputValue != null && producibleMediaTypes.isEmpty()) {
throw new HttpMessageNotWritableException("...." + valueType);
}
mediaTypesToUse = new ArrayList<>();
for (MediaType requestedType : requestedMediaTypes) {
for (MediaType producibleType : producibleMediaTypes) {
if (requestedType.isCompatibleWith(producibleType)) {
mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));
}
}
}
if (mediaTypesToUse.isEmpty()) {
if (outputValue != null) {
throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes);
}
return;
}
MediaType.sortBySpecificityAndQuality(mediaTypesToUse);
}
MediaType selectedMediaType = null;
for (MediaType mediaType : mediaTypesToUse) {
if (mediaType.isConcrete()) {
selectedMediaType = mediaType;
break;
}
else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) {
selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
break;
}
}
if (selectedMediaType != null) {
selectedMediaType = selectedMediaType.removeQualityValue();
//循环所有的转换器进行消息写会响应
for (HttpMessageConverter<?> converter : this.messageConverters) {
//判断转换器类型,进行类型转换
GenericHttpMessageConverter genericConverter =
(converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
if (genericConverter != null ?
((GenericHttpMessageConverter) converter).canWrite(declaredType, valueType, selectedMediaType) :
converter.canWrite(valueType, selectedMediaType)) {
//循环所有ResponseBodyAdvice切面实现类,判断符合处理该返回值则进行调用,按照排序的串行循环调用
outputValue = (T) getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType,
(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
inputMessage, outputMessage);
if (outputValue != null) {
//设置响应的Content-Disposition请求头
addContentDispositionHeader(inputMessage, outputMessage);
if (genericConverter != null) {
//调用转换器的方法将返回值写出响应
genericConverter.write(outputValue, declaredType, selectedMediaType, outputMessage);
}else {
//调用转换器的方法将返回值写出响应
((HttpMessageConverter) converter).write(outputValue, selectedMediaType, outputMessage);
}
if (logger.isDebugEnabled()) {
logger.debug(".....");
}
}
return;
}
}
}
if (outputValue != null) {
throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
}
}
总结
这里介绍的流程涉及到了几个扩展接口:
HttpMessageConverter转换器接口,可以自定义,开发起来比较复杂,内置的基本够用。
Validator 校验器接口,可以实现自定义校验,开发难度大,内置也基本够用
RequestBodyAdvice接口,在JSON请求入参序列化成对象前后做一些操作,可根据业务逻辑定制
ResponseBodyAdvice接口,在返回值写回给请求方前调用做一些操作,根据业务需要定制