springmvc 请求参数传递

前言

一直以来,每当自己学习框架之后。总是会有一种无力感,框架流程很复杂,虽然能够理清大概,但无论如何总是有一层模糊感,过一段时间如果不使用,之后便会忘记。总结其原因,可能是没有系统化的原因,很多碎片零散的躺在头脑中,确没有很流畅的流通。所以,在整理框架知识点的时候,尽可能系统,让碎片尽量多的连接。

这段时间做一个需求,需要对前台传到后台的参数进行转换。以前其实实现过很多这种功能,从功能的角度上来说,这并没有难点。但仔细一想,自己只知道参数传递的大概,对它确没有一个整体的认识,于是就想整理下这方面的知识点。

以下为大概的流程图,绘图网站:websequencediagrams

在这里插入图片描述

概览

springmvc 提供了HandlerMapping 接口,定义请求和处理程序之间的映射,实现方式多种多样,具体参考其具体实现,这里关注我们最常用的实现:RequestMappingHandlerMapping,即请求映射到方法上。 HandlerMapping.getHandler() 获取处理器,这个处理器是真正用来处理逻辑业务的。RequestMappingHandlerMappinghandlerHandlerMethod ,方法处理器。每一个handler都需要配备相对应的HandlerAdapter来对handler 进行适配,好让Handler 和其他类一起来完成请求工作。每一个HandlerAdapter都必须明确的指出自己支持的Handler。而支持HandlerMethodHandlerAdapter 便是AbstractHandlerMethodAdapter

/**
	 * This implementation expects the handler to be an {@link HandlerMethod}.
	 * @param handler the handler instance to check
	 * @return whether or not this adapter can adapt the given handler
	 */
	@Override
	public final boolean supports(Object handler) {
		return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
	}

	/**
	 * Given a handler method, return whether or not this adapter can support it.
	 * @param handlerMethod the handler method to check
	 * @return whether or not this adapter can adapt the given method
	 */
	protected abstract boolean supportsInternal(HandlerMethod handlerMethod);

其实现类可以对支持类进行进一步制约,使其支持更加精确的类。
但是AbstractHandlerMethodAdapter 只是一个抽象类,并没做出具体实现,具体实现交由其子类的handleInternal 来实现。其实现默认只有一个RequestMappingHandlerAdapter,所以就以这几个类着重展开分析。

从图中不难看出,参数的重点处理是在HandlerMethodArgumentResolverHandlerMethodReturnValueHandler 这两个接口中处理。

HandlerMethodArgumentResolver

HandlerMethodArgumentResolver 的实现非常丰富,这里关注最常用的几个RequestParamMethodArgumentResolverServletModelAttributeMethodProcessorRequestResponseBodyMethodProcessor

RequestMappingHandlerAdapter.getDefaultArgumentResolvers() 做了容错措施。
容错代码:
RequestParamMethodArgumentResolver默认同意解析简单类型,ServletModelAttributeMethodProcessor同意解析非简单类型。

// Catch-all
resolvers.add(new PrincipalMethodArgumentResolver());
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
resolvers.add(new ServletModelAttributeMethodProcessor(true));

RequestParamMethodArgumentResolver

其中RequestParamMethodArgumentResolver 默认用来处理@RequestParam 或者简单类型,

	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		if (parameter.hasParameterAnnotation(RequestParam.class)) {
			if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
				RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
				return (requestParam != null && StringUtils.hasText(requestParam.name()));
			}
			else {
				return true;
			}
		}
		else {
			if (parameter.hasParameterAnnotation(RequestPart.class)) {
				return false;
			}
			parameter = parameter.nestedIfOptional();
			if (MultipartResolutionDelegate.isMultipartArgument(parameter)) {
				return true;
			}
			else if (this.useDefaultResolution) {
				return BeanUtils.isSimpleProperty(parameter.getNestedParameterType());
			}
			else {
				return false;
			}
		}
	}

简单类型由这个解析器来提供解析,具体哪些是简单类型参考BeanUtils.isSimpleProperty()

ServletModelAttributeMethodProcessor

ServletModelAttributeMethodProcessor 用来处理@ModelAttribute 和非简单类型。
这个解析器还是非常重要的。


	/**
	 * Returns {@code true} if the parameter is annotated with
	 * {@link ModelAttribute} or, if in default resolution mode, for any
	 * method parameter that is not a simple type.
	 */
	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		return (parameter.hasParameterAnnotation(ModelAttribute.class) ||
				(this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType())));
	}

ServletModelAttributeMethodProcessor 继承了 ModelAttributeMethodProcessor,ModelAttributeMethodProcessor 为每一个属性创建了WebDataBinder,并从工厂中将配置好的类赋值给它,将参数转换和校验和WebDataBinder 关联,
具体绑定方法在bindRequestParameters()
接下来是一连串的校验等,直接来到DataBinder.applyPropertyValues()方法中。

protected void applyPropertyValues(MutablePropertyValues mpvs) {
		try {
			getPropertyAccessor().setPropertyValues(mpvs, isIgnoreUnknownFields(), isIgnoreInvalidFields());
		}
		catch (PropertyBatchUpdateException ex) {
			..........
		}
	}

这里的getPropertyAccessor() 实际上是将属性包装为一个BeanWrapper 并且给里面设置了ConversionService;


	protected AbstractPropertyBindingResult createBeanPropertyBindingResult() {
		BeanPropertyBindingResult result = new BeanPropertyBindingResult(getTarget(),
				getObjectName(), isAutoGrowNestedPaths(), getAutoGrowCollectionLimit());

		if (this.conversionService != null) {
			result.initConversion(this.conversionService);
		}
		.........

		return result;
	}

然后BeanWrapper 开始设置属性,BeanWrapper.setPropertyValues 的流程异常复杂,但是都会判断是否需要转换,来到AbstractNestablePropertyAccessor .convertIfNecessary()方法,这里看到,转换由typeConverterDelegate代理,

public <T> T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue,
			@Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException {

		// Custom editor for this type?
		PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);

		ConversionFailedException conversionAttemptEx = null;

		// No custom editor but custom ConversionService specified?
		ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
		if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
			.....
			if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
					....
					return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
				}
				......
			}
		}

		Object convertedValue = newValue;

		// Value not of required type?
		if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
			.......
			if (editor == null) {
				editor = findDefaultEditor(requiredType);
			}
			convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
		}

		boolean standardConversion = false;
		....

		return (T) convertedValue;
	}

方法中最重要的无非就是 propertyEditorRegistry 这个对象了,而这个对象就是这个代理的调用者BeanWrapper,可以在AbstractNestablePropertyAccessor的构造方法中看到这个创建。到了这里,就调用配置的ConversionService 来对参数进行转换。

其实这里我一直有一个疑问,就是PropertyEditor 是怎么加入BeanWrapper 中的,通过代码调试,发现,是在WebDataBinderFactory.createBinder() 创建·WebDataBinder` 就已经生成了。

public final WebDataBinder createBinder(
			NativeWebRequest webRequest, @Nullable Object target, String objectName) throws Exception {

		WebDataBinder dataBinder = createBinderInstance(target, objectName, webRequest);
		if (this.initializer != null) {
			this.initializer.initBinder(dataBinder, webRequest);
		}
		initBinder(dataBinder, webRequest);
		return dataBinder;
	}
@Override
	public void initBinder(WebDataBinder binder) {
	.....
		if (this.propertyEditorRegistrars != null) {
			for (PropertyEditorRegistrar propertyEditorRegistrar : this.propertyEditorRegistrars) {
				propertyEditorRegistrar.registerCustomEditors(binder);
			}
		}
	}

这里把binder暴露出去,让配置注册属性编辑器。一个例子

 initializer.setPropertyEditorRegistrar(binder-> binder.registerCustomEditor(String.class,new StringTrimmerEditor(true)));

这个函数参数就是propertyEditorRegistrar,曝出的binder 对象,可以寻找或者注入编辑器。而DataBinder.registerCustomEditor().的实现是:

	public void registerCustomEditor(@Nullable Class<?> requiredType, @Nullable String field, PropertyEditor propertyEditor) {
		getPropertyEditorRegistry().registerCustomEditor(requiredType, field, propertyEditor);
	}

	/**
	 * getInternalBindingResult() 获取bindResult,bindResult 创建beanwrapper 并放入转换器,
	 * getPropertyAccessor() 获取 beanwrapper,
	 */
	protected PropertyEditorRegistry getPropertyEditorRegistry() {
		if (getTarget() != null) {
			return getInternalBindingResult().getPropertyAccessor();
		}
		else {
			return getSimpleTypeConverter();
		}
	}

在这里,就已经把PropertyEditorConversionService 全部加进去了。如果不存在属性注入器,这里注册就不会发生,后面只注入ConversionService 也很合情合理。

接下来是参数校验和视图渲染等,这里不做介绍了。

RequestResponseBodyMethodProcessor

RequestResponseBodyMethodProcessor 用来处理携带@RequestBody的方法参数。


	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		return parameter.hasParameterAnnotation(RequestBody.class);
	}

这个对象的运行方式比上面的简单很多,重点在于请求时的处理方法readWithMessageConverters(),以及返回时的重点处理方法writeWithMessageConverters(),在这两个方法中,都会调用持有变量messageConverters 来对请求参数和返回参数进行转换。那么messageConverters 是在哪注入的呢?在RequestMappingHandlerAdapter.getDefaultArgumentResolvers()
RequestMappingHandlerAdapter.getDefaultReturnValueHandlers() 都会把配置的 参数转化器和返回转换器注入。而这两个方法会在afterPropertiesSet() 方法中调用,会在注入spring后执行此方法。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值