springmvc参数传递过程
前言
一直以来,每当自己学习框架之后。总是会有一种无力感,框架流程很复杂,虽然能够理清大概,但无论如何总是有一层模糊感,过一段时间如果不使用,之后便会忘记。总结其原因,可能是没有系统化的原因,很多碎片零散的躺在头脑中,确没有很流畅的流通。所以,在整理框架知识点的时候,尽可能系统,让碎片尽量多的连接。
这段时间做一个需求,需要对前台传到后台的参数进行转换。以前其实实现过很多这种功能,从功能的角度上来说,这并没有难点。但仔细一想,自己只知道参数传递的大概,对它确没有一个整体的认识,于是就想整理下这方面的知识点。
以下为大概的流程图,绘图网站:websequencediagrams
概览
springmvc 提供了HandlerMapping
接口,定义请求和处理程序之间的映射,实现方式多种多样,具体参考其具体实现,这里关注我们最常用的实现:RequestMappingHandlerMapping
,即请求映射到方法上。 HandlerMapping.getHandler()
获取处理器,这个处理器是真正用来处理逻辑业务的。RequestMappingHandlerMapping
的 handler
是 HandlerMethod
,方法处理器。每一个handler
都需要配备相对应的HandlerAdapter
来对handler
进行适配,好让Handler
和其他类一起来完成请求工作。每一个HandlerAdapter
都必须明确的指出自己支持的Handler
。而支持HandlerMethod
的HandlerAdapter
便是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
,所以就以这几个类着重展开分析。
从图中不难看出,参数的重点处理是在HandlerMethodArgumentResolver
和 HandlerMethodReturnValueHandler
这两个接口中处理。
HandlerMethodArgumentResolver
HandlerMethodArgumentResolver
的实现非常丰富,这里关注最常用的几个RequestParamMethodArgumentResolver
,ServletModelAttributeMethodProcessor
,RequestResponseBodyMethodProcessor
,
在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();
}
}
在这里,就已经把PropertyEditor
和 ConversionService
全部加进去了。如果不存在属性注入器,这里注册就不会发生,后面只注入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
后执行此方法。