RequestResponseBodyMethodProcessor类源码解析 参数解析器接口实现逻辑 返回值处理器接口解析逻辑 总结 这个类用于解析@RequestBody入参和@ResponseBody返回值解析问题。
RequestResponseBodyMethodProcessor类源码解析
- 参数解析器接口实现逻辑
- 返回值处理器接口解析逻辑
- 总结
这个类用于解析@RequestBody入参和@ResponseBody返回值解析问题。先查看一些类继承结构图:
如上图描述,以上几个类作用标记如图。标记几个类的有部分相同的代码逻辑,同时也只有以上几种情况下@ControllerAdvice注解的切面才会生效,其余情况不会的入参和返回值不会对其产生作用。
参数解析器接口实现逻辑
HandlerMethodArgumentResolver接口的supportsParameter方法实现逻辑//只支持参数使用
@RequestBody注解
@Overridepublic boolean supportsParameter(
MethodParameter parameter) {
return parameter.hasParameterAnnotation(
RequestBody.class);}
HandlerMethodArgumentResolver接口的resolveArgument方法实现逻辑@Overridepublic 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);}
使用转换器读取请求参数//读取请求入参
@Overrideprotected <
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方法,设置使用的组件,有的是自定义的
@Overridepublic 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#validatepublic 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注解
@Overridepublic boolean supportsReturnType(
MethodParameter returnType) {
return (
AnnotatedElementUtils.hasAnnotation(
returnType.getContainingClass(),
ResponseBody.class) ||
returnType.hasMethodAnnotation(
ResponseBody.class));}
处理返回值的逻辑handleReturnValue方法//构建读取组件和参数
@Overridepublic 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接口,在返回值写回给请求方前调用做一些操作,根据业务需要定制