RequestResponseBodyMethodProcessor类源码解析

RequestResponseBodyMethodProcessor类源码解析


这个类用于解析@RequestBody入参和@ResponseBody返回值解析问题。先查看一些类继承结构图:

在这里插入图片描述
如上图描述,以上几个类作用标记如图。标记几个类的有部分相同的代码逻辑,同时也只有以上几种情况下@ControllerAdvice注解的切面才会生效,其余情况不会的入参和返回值不会对其产生作用。

参数解析器接口实现逻辑

  1. HandlerMethodArgumentResolver接口的supportsParameter方法实现逻辑
//只支持参数使用@RequestBody注解
@Override
public boolean supportsParameter(MethodParameter parameter) {
	return parameter.hasParameterAnnotation(RequestBody.class);
}
  1. 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);
}
  1. 使用转换器读取请求参数
//读取请求入参
@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类。

  1. 获取数据板绑定器
//获取数据绑定器 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);
		}
	}
}
  1. 调用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接口扩展自定义的校验规则,自动会添加到默认实现类中。

返回值处理器接口解析逻辑

  1. 支持处理的条件supportsReturnType方法实现逻辑
//必须类上面或者方法上有@ResponseBody注解
@Override
public boolean supportsReturnType(MethodParameter returnType) {
	return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
			returnType.hasMethodAnnotation(ResponseBody.class));
}
  1. 处理返回值的逻辑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接口,在返回值写回给请求方前调用做一些操作,根据业务需要定制

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
@RequestBody注解用于将请求体中的数据绑定到方法的参数上。其源码实现主要涉及到RequestResponseBodyMethodProcessor。该实现了HandlerMethodArgumentResolver接口,具体负责处理方法参数的解析。 在RequestResponseBodyMethodProcessor中,supportsParameter方法用于判断是否支持解析某个参数。它通过判断方法参数上是否存在@RequestBody注解来进行判断。如果存在该注解,则返回true,表示该参数可被解析为请求体数据。 resolveArgument方法则是具体进行参数解析的方法。它首先会获取请求的Content-Type,判断是否为application/json或application/xml等型,以确保请求体的数据为JSON或XML格式。然后将请求体的数据转换为方法参数所需的对象。这里会使用HttpMessageConverter进行数据的转换,将请求体的数据转换为方法参数对应的对象。 综上所述,@RequestBody注解的源码实现主要通过RequestResponseBodyMethodProcessor来实现,它负责判断是否支持解析参数以及将请求体数据转换为方法参数所需的对象。<span class="em">1</span><span class="em">2</span> #### 引用[.reference_title] - *1* *2* [Spring 注解面面通 之 @RequestBody参数绑定源码解析](https://blog.csdn.net/securitit/article/details/110705815)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值