springboot-方法处理2-参数解析器

1.参数解析器简介

上一篇章稍微研究了方法处理器(HandlerMethod),本篇稍微研究参数解析器(HandlerMethodArgumentResolver)。参数解析器主要用于根据目标方法的参数描述(比如参数类型/参数名/注解),从各种http对象(比如request/session/header)中获取参数值;

1.1.类结构

参数解析器结构

1.2.功能说明(重点)

名称以ReturnValueHandler结尾的是单一的参数解析器,名称以MethodProcessor结尾的既是参数解析器,也是返回值处理器;

分类名称支持的类型功能
获取request对象ServletRequestMethodArgumentResolver 直接获取Request,HttpSession, PushBuilder,HttpMethod等对象 参数解析器
获取response对象ServletResponseMethodArgumentResolver 直接获取ServletResponse, OutputStream, Writer对象 参数解析器
获取requestHeader参数RequestHeaderMethodArgumentResolver@RequestHeader标注的参数(非Map类型)参数解析器
RequestHeaderMapMethodArgumentResolver 1.@RequestHeader标注的Map参数(同参数取第一个值) 2.@RequestHeader标注的MultiValueMap参数(同参数取所有值) 参数解析器
获取requestParamter参数RequestParamMethodArgumentResolver 1.@RequestParam标注的普通参数(非Map类型) 2.@RequestParam标注的MultipartFile参数 3.没用@RequestParam标注的简单类型(int/long) 4.@RequestParam标注的Map类型参数(需在注解中指定参数名) 参数解析器
RequestParamMapMethodArgumentResolver 1.@RequestParam标注的Map类型参数(没有指定参数名。同参数取第一个值,文件则取所有文件) 2.@RequestParam标注MultiValueMap参数(没有指定参数名。同参数取所有值) 参数解析器
ModelAttributeMethodProcessor (从model中获取值) 1.@ModelAttribute标注的参数 2.@ModelAttribute标注的方法 参数解析器&返回值处理器
ServletModelAttributeMethodProcessor (依次从url/requestParameter/model中获取值) 1.@ModelAttribute标注的参数 2.@ModelAttribute标注的方法 3.所有非简单类型/简单类型数组(annotationNotRequired=true) 参数解析器&返回值处理器
ExpressionValueMethodArgumentResolver@Value标注的参数(支持${...}表#{...}表达式)参数解析器
获取requestAttribute参数ServletWebArgumentResolverAdapter WebArgumentResolver支持的类型 实现WebArgumentResolver自定义参数解析 参数解析器适配器
RequestAttributeMethodArgumentResolver@RequestAttribute标注的参数参数解析器
获取serssionAttribute参数SessionAttributeMethodArgumentResolver@SessionAttribute标注的参数参数解析器
获取url参数PathVariableMethodArgumentResolver 1.@PathVariable标注的普通参数(非Map类型) 2.@PathVariable标注的Map类型参数(需在注解中指定参数名) 参数解析器
PathVariableMapMethodArgumentResolver@PathVariable标注的Map类型参数(没有指定参数名)参数解析器
MatrixVariableMethodArgumentResolver 1.@MatrixVariable标注的普通参数(非Map类型) 2.@MatrixVariable标注的Map类型参数(需在注解中指定参数名) 参数解析器
MatrixVariableMapMethodArgumentResolver@MatrixVariable标注的Map类型参数(没有指定参数名)参数解析器
获取requestBody参数RequestPartMethodArgumentResolver1.@RequestPart注解标注的参数 2.MultipartFile类型参数 3.Servlet3.0的javax.servlet.http.Part类型参数 参数解析器
RequestResponseBodyMethodProcessor1.@RequestBody标注的参数 2.@ResponseBody标注的方法 参数解析器&返回值处理器
HttpEntityMethodProcessor1.HttpEntity和RequestEntity类型的参数 2.返回HttpEntity和ResponseEntity类型的方法 参数解析器&返回值处理器

2.参数解析器部分源码

2.1.根据参数名获取值

AbstractNamedValueMethodArgumentResolver

扩展根据参数名获取参数值的功能。参数名一般为参数的注解配置的名称,如果没有注解或者注册没有配置名称,则为参数的名称。所有根据参数名称获取参数值的参数解析器都是它的子类。具体过程如下:

  • 获取目标获取目标方法参数的基本信息(参数名称&是否必须&默认值);
  • 解析配置的参数名(通常由注解配置,参数名支持表达式);
  • 根据目标方法的参数名获取参数值;
  • 如果参数值为空,则先解析配置的默认值,然后处理参数值缺失,最后处理空值;
  • 解析完成之后处理参数值;
public abstract class AbstractNamedValueMethodArgumentResolver implements HandlerMethodArgumentResolver {
    //**获取目标方法参数注解配置的属性
	@Nullable
	private final ConfigurableBeanFactory configurableBeanFactory;
    //**解析注解配置属性中的表达式
	@Nullable
	private final BeanExpressionContext expressionContext;
    //**缓存解析过的目标方法参数
	private final Map<MethodParameter, NamedValueInfo> namedValueInfoCache = new ConcurrentHashMap<>(256);

	@Override
	@Nullable
	public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
        //**获取目标方法参数的基本信息(参数名称&是否必须&默认值)
		NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
		//**是否是Optional类型参数
		MethodParameter nestedParameter = parameter.nestedIfOptional();
        
        //**解析参数名(通常由注解配置,参数名支持表达式)
		Object resolvedName = resolveStringValue(namedValueInfo.name);
		...
        
        //**根据目标方法的参数名获取参数值
		Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
		if (arg == null) {
    		//**如果参数值为空&s配置了默认值,则解析默认值
			if (namedValueInfo.defaultValue != null) {
				arg = resolveStringValue(namedValueInfo.defaultValue);
			}
			//**如果参数值为空&参数是必须的&参数不是Optional类型,则处理参数值缺失
			else if (namedValueInfo.required && !nestedParameter.isOptional()) {
				handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
			}
			//**如果参数值为空,则处理空值
			arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
		}
		else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
			arg = resolveStringValue(namedValueInfo.defaultValue);
		}
		...

        //**解析完成之后处理参数值
		handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);

		return arg;
	}

	//**获取目标方法参数信息(NamedValueInfo封装了目标方法参数名称&是否必须&默认值),
	private NamedValueInfo getNamedValueInfo(MethodParameter parameter) {
		NamedValueInfo namedValueInfo = this.namedValueInfoCache.get(parameter);
		if (namedValueInfo == null) {
			namedValueInfo = createNamedValueInfo(parameter);
			namedValueInfo = updateNamedValueInfo(parameter, namedValueInfo);
			this.namedValueInfoCache.put(parameter, namedValueInfo);
		}
		return namedValueInfo;
	}

	//**根据目标方法参数信息创建NamedValueInfo对象,获取目标方法参数的名称/是否必须/默认值
	protected abstract NamedValueInfo createNamedValueInfo(MethodParameter parameter);

	//**更新目标方法的参数信息
	private NamedValueInfo updateNamedValueInfo(MethodParameter parameter, NamedValueInfo info) {
		String name = info.name;
		if (info.name.isEmpty()) {
			name = parameter.getParameterName();
			...
		}
		String defaultValue = (ValueConstants.DEFAULT_NONE.equals(info.defaultValue) ? null : info.defaultValue);
		return new NamedValueInfo(name, info.required, defaultValue);
	}

	//**解析参数配置的名称/默认值
	@Nullable
	private Object resolveStringValue(String value) {
		if (this.configurableBeanFactory == null) {
			return value;
		}
		String placeholdersResolved = this.configurableBeanFactory.resolveEmbeddedValue(value);
		BeanExpressionResolver exprResolver = this.configurableBeanFactory.getBeanExpressionResolver();
		if (exprResolver == null || this.expressionContext == null) {
			return value;
		}
		return exprResolver.evaluate(placeholdersResolved, this.expressionContext);
	}

	//**根据目标方法的参数名获取参数值
	@Nullable
	protected abstract Object resolveName(String name, MethodParameter parameter, NativeWebRequest request)
			throws Exception;

	//**处理参数值缺失(目标方法的参数不能为空 & 没有默认值 & resolveName返回null时调用)
	protected void handleMissingValue(String name, MethodParameter parameter, NativeWebRequest request)
			throws Exception {

		handleMissingValue(name, parameter);
	}

	//**处理参数值缺失(目标方法的参数不能为空 & 没有默认值 & resolveName返回null时调用)
	protected void handleMissingValue(String name, MethodParameter parameter) throws ServletException {
		throw new ServletRequestBindingException("Missing argument '" + name +
				"' for method parameter of type " + parameter.getNestedParameterType().getSimpleName());
	}

	//**解析之后参数值为空,进行额外处理
	@Nullable
	private Object handleNullValue(String name, @Nullable Object value, Class<?> paramType) {
		if (value == null) {
			if (Boolean.TYPE.equals(paramType)) {
				return Boolean.FALSE;
			}
			...
		}
		return value;
	}

	//**解析之后处理参数值
	protected void handleResolvedValue(@Nullable Object arg, String name, MethodParameter parameter,
			@Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest) {
	}
}

2.2.获取body参数

AbstractMessageConverterMethodArgumentResolver

扩展解析request中body内容,并转换为目标方法的参数类型的功能。所有从body解析参数的参数解析器都是它的子类。具体过程如下:

  • 获取基本信息(http请求content-type、header和body,并获取目标方法参数的类对象targetClass);
  • 根据content-type和targetClass找到支持的消息转换器;
  • 如果body的内容为空,则直接调用解析之后调用RequestResponseBodyAdviceChain.handleEmptyBody处理空值;
  • 解析之前调用RequestResponseBodyAdviceChain.beforeBodyRead进行处理;
  • 调用消息转换器read()从body中解析目标方法的参数值;
  • 解析之后调用RequestResponseBodyAdviceChain.afterBodyRead进行处理;
public abstract class AbstractMessageConverterMethodArgumentResolver implements HandlerMethodArgumentResolver {
    //**支持的http方法
	private static final Set<HttpMethod> SUPPORTED_METHODS =
			EnumSet.of(HttpMethod.POST, HttpMethod.PUT, HttpMethod.PATCH);
    //**空值
	private static final Object NO_VALUE = new Object();
    //**消息转换器,读取body内容并转换为目标方法的参数
	protected final List<HttpMessageConverter<?>> messageConverters;
    //**支持的http Media类型
	protected final List<MediaType> allSupportedMediaTypes;
    //**读取body内容前/后进行额外的处理
	private final RequestResponseBodyAdviceChain advice;

    //**获取支持的Media类型(实际是HttpMessageConverter支持的Media类型)
	private static List<MediaType> getAllSupportedMediaTypes(List<HttpMessageConverter<?>> messageConverters) {
		Set<MediaType> allSupportedMediaTypes = new LinkedHashSet<>();
		for (HttpMessageConverter<?> messageConverter : messageConverters) {
			allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
		}
		List<MediaType> result = new ArrayList<>(allSupportedMediaTypes);
		MediaType.sortBySpecificity(result);
		return Collections.unmodifiableList(result);
	}
    
    //**根据目标方法参数读取request中body的内容
	@Nullable
	protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
			Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
        //**获取http header中content-type,默认为application/octet-stream
		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对象
		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();
		}
        
        //**获取http方法
		HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);
		Object body = NO_VALUE;

		EmptyBodyCheckingHttpInputMessage message;
		try {
		    //**获取request中的headers和body;
			message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
            
            //**遍历所有的消息转换器,找到支持http content-type和目标方法的参数类型的消息转换器,解析目标方法的参数值
			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()) { //**body内容不为空,则解析目标方法的参数值
					    //**解析之前进行处理
						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;
				}
			}
		}
		...
		return body;
	}

	//**获取HttpInputMessage
	protected ServletServerHttpRequest createInputMessage(NativeWebRequest webRequest) {
		HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
		Assert.state(servletRequest != null, "No HttpServletRequest");
		return new ServletServerHttpRequest(servletRequest);
	}

	//**参数验证
	protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
		Annotation[] annotations = parameter.getParameterAnnotations();
		for (Annotation ann : annotations) {
			Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
			if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
				Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
				Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
				binder.validate(validationHints);
				break;
			}
		}
	}

	//**参数验证错误是否抛出异常(如果参数后面是一个BindingResult参数,则不会抛出异常)
	protected boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter parameter) {
		int i = parameter.getParameterIndex();
		Class<?>[] paramTypes = parameter.getExecutable().getParameterTypes();
		//**如果参数后面是一个BindingResult(Errors的子类)参数,则不会抛出异常
		boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1]));
		return !hasBindingResult;
	}

	//**如果目标方法的参数是Optional类型,则把解析的参数值包装成Optional
	@Nullable
	protected Object adaptArgumentIfNecessary(@Nullable Object arg, MethodParameter parameter) {
		if (parameter.getParameterType() == Optional.class) {
			if (arg == null || (arg instanceof Collection && ((Collection<?>) arg).isEmpty()) ||
					(arg instanceof Object[] && ((Object[]) arg).length == 0)) {
				return Optional.empty();
			}
			else {
				return Optional.of(arg);
			}
		}
		return arg;
	}
}

2.3.参数解析器适配器

A.AbstractWebArgumentResolverAdapter

把WebArgumentResolver转换为参数解析器的适配器。需要和自定义的WebArgumentResolver一起使用,解析自定义的参数类型;

public abstract class AbstractWebArgumentResolverAdapter implements HandlerMethodArgumentResolver {
    //**WebArgumentResolver
	private final WebArgumentResolver adaptee;

    //**使用WebArgumentResolver判断是否能解析目标参数
	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		try {
			NativeWebRequest webRequest = getWebRequest();
			Object result = this.adaptee.resolveArgument(parameter, webRequest);
			if (result == WebArgumentResolver.UNRESOLVED) {
				return false;
			}
			else {
				return ClassUtils.isAssignableValue(parameter.getParameterType(), result);
			}
		}
		...
	}

	//**使用WebArgumentResolver解析目标参数
	@Override
	@Nullable
	public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

		Class<?> paramType = parameter.getParameterType();
		Object result = this.adaptee.resolveArgument(parameter, webRequest);
		...
		}
		return result;
	}

    //**获取NativeWebRequest
	protected abstract NativeWebRequest getWebRequest();

}

B.WebArgumentResolver

WebArgumentResolver是一个函数式接口,是spring预留用于解析用户自定义参数类型的SPI。使用该接口可以很方便的自定义参数解析器,解析自定义的参数类型。我觉得自定义参数解析器应该优先考虑该接口;

@FunctionalInterface
public interface WebArgumentResolver {

	//**解析目标方法的参数
	@Nullable
	Object resolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) throws Exception;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot中,你可以使用MultipartResolver来解析文件上传。MultipartResolver是Spring提供的一个接口,它用于处理multipart请求,包括文件上传。 首先,你需要在你的Spring Boot应用中添加相关的依赖。可以在你的`pom.xml`文件中添加以下依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency> ``` 接下来,在你的Spring Boot配置类中,你可以配置一个`MultipartResolver` bean。例如,你可以创建一个名为`multipartResolver`的bean: ```java import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.multipart.commons.CommonsMultipartResolver; @Configuration public class AppConfig { @Bean public CommonsMultipartResolver multipartResolver() { CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(); multipartResolver.setMaxUploadSize(5242880); // 设置最大上传文件大小为5MB return multipartResolver; } } ``` 在上面的示例中,我们使用了`CommonsMultipartResolver`作为`MultipartResolver`的实现。还可以根据需要进行其他配置,比如设置最大上传文件大小。 最后,在你的控制器中,你可以使用`@RequestParam`注解来接收文件上传的参数。例如,如果你要上传一个文件和其他表单参数,可以这样做: ```java import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; @RestController public class FileUploadController { @PostMapping("/upload") public String handleFileUpload(@RequestParam("file") MultipartFile file, @RequestParam("name") String name) { // 处理文件上传逻辑 // 可以通过file和name参数获取上传的文件和其他表单参数 return "File uploaded successfully!"; } } ``` 在上面的示例中,`@RequestParam("file")`用于接收上传的文件,`@RequestParam("name")`用于接收其他表单参数。 这样,当你发送一个包含文件上传的POST请求到`/upload`路径时,Spring Boot将自动解析文件上传,并将文件和其他表单参数传递给`handleFileUpload`方法进行处理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值