Spring MVC 参数解析

组件

HandlerMethodArgumentResolverComposite

适配方法参数解析器组件,保存所有参数解析器

public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
		// 持有的参数解析器
		private final List<HandlerMethodArgumentResolver> argumentResolvers = new LinkedList<>();
		// 缓存解析过的参数解析器
		private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache =
			new ConcurrentHashMap<>(256);

}

HandlerMethodArgumentResolver

方法参数解析器,将request中数据解析为方法参数

public interface HandlerMethodArgumentResolver {
	// 判断是否支持方法参数解析
	boolean supportsParameter(MethodParameter parameter);
	// 解析参数
	Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;

}

MethodParameter

处理器中的方法参数描述

public class MethodParameter {
	// 参数属于的方法
	private final Executable executable;
	// 参数在方法中的位置
	private final int parameterIndex;
	@Nullable
	private volatile Parameter parameter;
	// 参数类型
	private volatile Class<?> parameterType;
}

NamedValueInfo

表示有关命名值的信息

protected static class NamedValueInfo {
	// 参数名
	private final String name;
	// 是否必要
	private final boolean required;
	// 默认值
	@Nullable
	private final String defaultValue;
}	

DataBinder

允许在target上设置属性值的绑定器,包括对验证和绑定结果分析的支持。

public class DataBinder implements PropertyEditorRegistry, TypeConverter {
	// 目标对象
	private final Object target;
	// 对象名
	private final String objectName;
}

参数处理流程

  1. 处理@InitBinder(用于数据绑定、设置数据转换器)注解的方法,先从缓存获取,从全局获取,从当前类获取,最后返回
InitBinderDataBinderFactory
private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
	// 获得处理方法类型
	Class<?> handlerType = handlerMethod.getBeanType();
	// 从适配器缓存获取
	Set<Method> methods = this.initBinderCache.get(handlerType);
	if (methods == null) {
		// 缓存无则将当前类@InitBinder修饰的方法加入缓存
		methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
		this.initBinderCache.put(handlerType, methods);
	}
	List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();
	// @ControllerAdvice修饰中全局添加
	this.initBinderAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
		if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
			Object bean = controllerAdviceBean.resolveBean();
			for (Method method : methodSet) {
				initBinderMethods.add(createInitBinderMethod(bean, method));
			}
		}
	});
	// 添加当前类中的@InitBinder方法
	for (Method method : methods) {
		Object bean = handlerMethod.getBean();
		initBinderMethods.add(createInitBinderMethod(bean, method));
	}
	// 返回InitBinderDataBinderFactory
	return createDataBinderFactory(initBinderMethods);
}
  1. 处理注解@ModelAttribute(用于将方法的参数或方法的返回值绑定到指定的模型属性上)相关内容(@SessionAttributes,@ControllerAdvice),将该注解修饰的方法放入当前适配器缓存,返回的ModelFactory持有了@SessionAttributes处理器和上一步生成的InitBinderDataBinderFactory
private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
	// 从适配器获得@SessionAttributes处理器
	SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
	// 实际处理器类型
	Class<?> handlerType = handlerMethod.getBeanType();
	// 从适配器获得@ModelAttribute的缓存
	Set<Method> methods = this.modelAttributeCache.get(handlerType);
	// 如果为空
	if (methods == null) {
		// 遍历实际处理器类,获得非@RequestMapper且有@ModelAttribute注解的方法
		methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
		// 放入缓存
		this.modelAttributeCache.put(handlerType, methods);
	}
	List<InvocableHandlerMethod> attrMethods = new ArrayList<>();
	// 从被@ControllerAdvice修饰的类中获取全局的@ModelAttribute方法
	this.modelAttributeAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
		if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
			Object bean = controllerAdviceBean.resolveBean();
			for (Method method : methodSet) {
				attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
			}
		}
	});
	// 获得当前处理器中获得@ModelAttribute方法
	for (Method method : methods) {
		Object bean = handlerMethod.getBean();
		attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
	}
	return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
}
  1. invokeHandlerMethod方法中为模型赋值语句,重定向属性FlashMap,SessionAttribute中获取值,ModelAttribute中获取值(调用invokeForRequest方法反射执行@ModelAttribute修饰的方法)
// 从request中获取重定向参数FlashMap存入Model
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
// 初始模型数据
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
// 重定向时忽略默认Model,该值默认为false
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);


// 初始模型数据
public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod)
			throws Exception {
	// 从请求中获取session属性
	Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
	// 放入Model中
	container.mergeAttributes(sessionAttributes);
	// 调用@ModelAttribute修饰的方法,传入model中
	invokeModelAttributeMethods(request, container);
	
	// 遍历sessionAttribute属性给model赋值
	for (String name : findSessionAttributeArguments(handlerMethod)) {
		if (!container.containsAttribute(name)) {
			Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
			if (value == null) {
				throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name);
			}
			container.addAttribute(name, value);
		}
	}
}

// 调用ModelAttribute修饰的方法,将属性值赋值给container中的Model
private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer container)
			throws Exception {
	// 当前ModelFactory中的方法不为空
	while (!this.modelMethods.isEmpty()) {
		// 从ModelMethod中获取名称不在container中的方法
		InvocableHandlerMethod modelMethod = getNextModelMethod(container).getHandlerMethod();
		ModelAttribute ann = modelMethod.getMethodAnnotation(ModelAttribute.class);
		Assert.state(ann != null, "No ModelAttribute annotation");
		// 如果容器中已存在ModelAttribute修饰的属性
		if (container.containsAttribute(ann.name())) {
			// 是否禁用数据绑定
			if (!ann.binding()) {
				container.setBindingDisabled(ann.name());
			}
			continue;
		}
		// 调用invokeForRequest方法获得注解对应方法的返回值
		Object returnValue = modelMethod.invokeForRequest(request, container);
		// 如果方法返回类型不为空
		if (!modelMethod.isVoid()){
			// 获得属性名称,如果注解设置的value使用value,否则为返回类型小写
			String returnValueName = getNameForReturnValue(returnValue, modelMethod.getReturnType());
			if (!ann.binding()) {
				container.setBindingDisabled(returnValueName);
			}
			if (!container.containsAttribute(returnValueName)) {
				// 为model添加属性
				container.addAttribute(returnValueName, returnValue);
			}
		}
	}
}
  1. 获取当前方法的参数,方法参数为空直接返回,遍历解析:如providedArgs已提供则continue,否则调用参数解析器解析(从缓存找,未找到则遍历参数解析器集合寻找合适的参数解析器)

protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
	// 获得当前处理方法的参数
	MethodParameter[] parameters = getMethodParameters();
	// 为null返回空值
	if (ObjectUtils.isEmpty(parameters)) {
		return EMPTY_ARGS;
	}
	// 存放方法参数
	Object[] args = new Object[parameters.length];
	// 遍历解析参数
	for (int i = 0; i < parameters.length; i++) {
		// 按顺序获得方法参数
		MethodParameter parameter = parameters[i];
		// 设置参数名查找器
		parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
		// 从提供的参数搜索
		args[i] = findProvidedArgument(parameter, providedArgs);
		if (args[i] != null) {
			continue;
		}
		// 所有参数解析器都不支持抛出异常
		if (!this.resolvers.supportsParameter(parameter)) {
			throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
		}
		try {
			// 从缓存找,未找到遍历参数解析器,调用参数解析器的resolveArgument方法
			args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
		}
		···抛出异常
	}
	return args;
}
  1. 参数解析器解析参数,以RequestParamMethodArgumentResolver为例,实例调用的是AbstractNamedValueMethodArgumentResolver中的resolveArgument方法。首先根据方法参数获得命名值信息,然后解析命名值(存在占位符的参数名),根据参数名从请求中获取参数值,处理参数值为空的情况,最后处理@InitBinder进行数据绑定和转换
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
	// 获得参数命名值信息(参数对应注解信息 name、required、defaultValue),无注解则使用parameterName
	NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
	// 支持Optional
	MethodParameter nestedParameter = parameter.nestedIfOptional();
	// 参数注解中占位符处理
	Object resolvedName = resolveStringValue(namedValueInfo.name);
	// 参数名解析失败
	if (resolvedName == null) {
		throw new IllegalArgumentException(
				"Specified name must not resolve to null: [" + namedValueInfo.name + "]");
	}
	
	// @RequestParam,通过RequestParamMethodArgumentResolver:从request中parameterMap获取url?之后的参数,基本类型自动处理无需@RequestParam
	// @PathVariable,通过PathVariableMethodArgumentResolver:从request中Attribute中获取uriTemplateVariables
	// @MatrixVariable,通过MatrixVariableMethodArgumentResolver:从request中Attribute中获取matrixVariables
	Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
	// 如果参数为null则走默认值
	if (arg == null) {
		// 解析默认值(可能存在占位符)
		if (namedValueInfo.defaultValue != null) {
			arg = resolveStringValue(namedValueInfo.defaultValue);
		}
		// 如果必须有值且变量不为Optional类型抛出异常ServletRequestBindingException
		else if (namedValueInfo.required && !nestedParameter.isOptional()) {
			handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
		}
		// 如果为boolean类型返回false其他基本类型抛出异常IllegalStateException
		arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
	}
	// 参数值为""且存在默认值则使用默认值
	else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
		arg = resolveStringValue(namedValueInfo.defaultValue);
	}
	// 如果存在@InitBinder,进行数据绑定
	if (binderFactory != null) {
		// 执行@InitBinder修饰的value为空或value=namedValueInfo.name的方法
		WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
		try {
			// 将从request请求获得的arg转换为对应参数类型的值
			arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
		}
		···抛出异常
	}
	// PathVariableMethodArgumentResolver类使用,将arg存入request的Attribute中
	handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);

	return arg;
}

更多

  1. 对于默认的Map入参,使用MapMethodProcessor处理,直接返回ModelAndViewContainer的modelMap。
  2. 处理器方法支持一些固定参数作为入参。如通过ServletRequestMethodArgumentResolver处理。
  3. 对于@RequestBody注解等,使用HttpMessageConverter来完成参数转换。
  4. 一般实体类如User等,使用ModelAttributeMethodProcessor来解析,如果在ModelAttribute中未找到则创建一个空的实体类,通过WebDataBinder将request请求中的参数绑定到实体类对应的字段。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值