SpringMvc @RequestParam工作流程
SpringMvc的RequestParam注解用法,这里不作记录,只是记录下Spring如何识别@RequestParam注解以及怎么工作的;
通过DEBUG,简单绘制了SpringMvc 参数转换 的时序图,这里就从 InvocableHandlerMethod 的 getMethodArgumentValues 方法进行分析;
invokeForRequest顾名思义,就是SpringMvc执行请求对应方法的逻辑,先看第一步,请求request对象的参数如何对应上@Controller方法的入参;
一。InvocableHandlerMethod 的 getMethodArgumentValues
private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// request为ServletWebRequest对象,持有原生的request和response
//之前博客介绍过 HandlerMethod对象, 这里就是间接获取了HandlerMethod的parameters对象
MethodParameter[] parameters = getMethodParameters();
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
//解析MethodParameter的类型,也就是Controller中方法的入参类型
GenericTypeResolver.resolveParameterType(parameter, getBean().getClass());
//因为providedArgs从之前传下来就是空数组,下面方法直接会返回null
args[i] = resolveProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
//this为ServletInvocableHandlerMethod对象,argumentResolvers为HandlerMethodArgumentResolverComposite对象
//后面会分析该对象以及该方法,Tips2
if (this.argumentResolvers.supportsParameter(parameter)) {
try {
//从缓存中得到对应的参数解析器,并调用resolveArgument方法解析;这里可以跳到Tips3去看
args[i] = this.argumentResolvers.resolveArgument(
parameter, mavContainer, request, this.dataBinderFactory);
continue;
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug(getArgumentResolutionErrorMessage("Error resolving argument", i), ex);
}
throw ex;
}
}
if (args[i] == null) {
String msg = getArgumentResolutionErrorMessage("No suitable resolver for argument", i);
throw new IllegalStateException(msg);
}
}
return args;
}
Tips2.分析 this.argumentResolvers.supportsParameter(parameter)方法:
HandlerMethodArgumentResolverComposite 的 supportsParameter 方法:
public boolean supportsParameter(MethodParameter parameter) {
//判断根据MethodParameter得到对应的解析器是否不为空
return (getArgumentResolver(parameter) != null);
}
查看下面方法,缓存的地方跳过,第一次都没有缓存,所以会遍历 HandlerMethodArgumentResolverComposite 的 argumentResolvers 来判断是否能够支持解析入参;
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
if (result == null) {
for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {
if (logger.isTraceEnabled()) {
logger.trace("Testing if argument resolver [" + methodArgumentResolver + "] supports [" +
parameter.getGenericParameterType() + "]");
}
if (methodArgumentResolver.supportsParameter(parameter)) {
//RequestParamMethodArgumentResolver解析器支持对方法入参@RequestParam的解析,将对应的解析器存入缓存下次直接使用并跳出循环
result = methodArgumentResolver;
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}
那SpringMvc已经注册了哪些 方法参数解析器呢?Spring4.2默认是26个,这个看解析器名字可以猜测RequestParam开头的是用来解析@RequestParam注解的参数,还有如PathVariable开头的、HttpEntitiy开头的;
ServletModelAttributeMethodProcessor是个很厉害的解析器,用于将请求中的参数映射到方法入参的JavaBean的属性上;
就以RequestParamMethodArgumentResolver 解析器的supportsParameter方法来查看; 可看到该解析器确实支持@RequestParam类型的参数解析
public boolean supportsParameter(MethodParameter parameter) {
//判断MethodParameter是否有注解RequestParam 也就是方法上入参是否有@RequestParam if (parameter.hasParameterAnnotation(RequestParam.class)) { if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) { String paramName = parameter.getParameterAnnotation(RequestParam.class).name(); return StringUtils.hasText(paramName); } else { return true; //通常标注了@RequestParam都会返回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; } } }
Tip3. RequestParamMethodArgumentResolver 的 resolveArgument 会调用父接口 AbstractNamedValueMethodArgumentResolver中的方法;
先介绍下NamedValueInfo类,就是@RequestParam的三个属性name、required、defaultValue 的实体类
1 public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
2 NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
3 //NamedValueInfo是@RequestParam注解的javaBean
4 NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
5 MethodParameter nestedParameter = parameter.nestedIfOptional();
6 //解析RequestParam注解的name属性,支持#{}、${}这种形式并解析这种;
7 Object resolvedName = resolveStringValue(namedValueInfo.name);
8 if (resolvedName == null) {
9 throw new IllegalArgumentException(
10 "Specified name must not resolve to null: [" + namedValueInfo.name + "]");
11 }
12 //真正从request中 getParameterValues获取对应参数,返回值是String
13 Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
//请求中不包含对应参数,required ==true且默认值为null就抛出异常
14 if (arg == null) {
15 if (namedValueInfo.defaultValue != null) {
16 arg = resolveStringValue(namedValueInfo.defaultValue);
17 }
18 else if (namedValueInfo.required && !nestedParameter.isOptional()) {
19 handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
20 }
21 arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
22 }
23 else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
24 arg = resolveStringValue(namedValueInfo.defaultValue);
25 }
26
27 if (binderFactory != null) {
28 WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
29 try {
//这里理解为上面获取到请求中对应参数,进行类型转换,因为获取到的都是String类型的;
//DataBinder的类型转换过程较为复杂,查看 二.
30 arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
31 }
32 catch (ConversionNotSupportedException ex) {
33 throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
34 namedValueInfo.name, parameter, ex.getCause());
35 }
36 catch (TypeMismatchException ex) {
37 throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
38 namedValueInfo.name, parameter, ex.getCause());
39
40 }
41 }
42 //空实现 留给子类实现
43 handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
44
45 return arg;
46 }
二。ExtendedServletRequestDataBinder 的 convertIfNecessary方法;
public <T> T convertIfNecessary(Object value, Class<T> requiredType, MethodParameter methodParam)
throws TypeMismatchException {
return getTypeConverter().convertIfNecessary(value, requiredType, methodParam);
}
首先是 getTypeConverter : 得到SimpleTypeConverter对象
1 protected TypeConverter getTypeConverter() {
//target对象Spring创建时候传入的为null,所以执行下面的getSimpleTypeConverter
2 if (getTarget() != null) {
3 return getInternalBindingResult().getPropertyAccessor();
4 }
5 else {
6 return getSimpleTypeConverter();
7 }
8 }
9
10 protected SimpleTypeConverter getSimpleTypeConverter() {
11 if (this.typeConverter == null) {
12 this.typeConverter = new SimpleTypeConverter();
13 if (this.conversionService != null) {
//将ExtendedServletRequestDataBinder的conversionService给typeConverter
14 this.typeConverter.setConversionService(this.conversionService);
15 }
16 }
17 return this.typeConverter;
18 }
其次是 SimpleTypeConverter 的 convertIfNecessary 方法:
public <T> T convertIfNecessary(Object value, Class<T> requiredType, MethodParameter methodParam)
throws TypeMismatchException {
// value为请求中获取的属性,requiredType为解析的Controller中该参数对应的类型
return doConvert(value, requiredType, methodParam, null);
}
private <T> T doConvert(Object value, Class<T> requiredType, MethodParameter methodParam, Field field)
throws TypeMismatchException {
try {
if (field != null) {
return this.typeConverterDelegate.convertIfNecessary(value, requiredType, field);
}
else {
//其typeConverterDelegate为TypeConverterDelegate对象;
return this.typeConverterDelegate.convertIfNecessary(value, requiredType, methodParam);
}
}
catch (ConverterNotFoundException ex) {
throw new ConversionNotSupportedException(value, requiredType, ex);
}
catch (ConversionException ex) {
throw new TypeMismatchException(value, requiredType, ex);
}
catch (IllegalStateException ex) {
throw new ConversionNotSupportedException(value, requiredType, ex);
}
catch (Throwable ex) {
throw new TypeMismatchException(value, requiredType, ex);
}
}
接着将 参数 转换工作交给 TypeConverterDelegate ,
具体的 convertIfNecessary 方法篇幅太长
public <T> T convertIfNecessary(Object newValue, Class<T> requiredType, MethodParameter methodParam)
throws IllegalArgumentException {
return convertIfNecessary(null, null, newValue, requiredType,
(methodParam != null ? new TypeDescriptor(methodParam) : TypeDescriptor.valueOf(requiredType)));
}
public <T> T convertIfNecessary(String propertyName, Object oldValue, Object newValue,
Class<T> requiredType, TypeDescriptor typeDescriptor) throws IllegalArgumentException {
PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
ConversionFailedException conversionAttemptEx = null;
//this为typeConverterDelegate对象,this.propertyEditorRegistry对象为SimpleTypeConverter
//SimpleTypeConverter对象的conversionService就是MVC:annotation-driven注册的ConversionService对象
//具体实现是 DefaultFormattingConversionService这个类
//至于注入方式 通过ConfigurableWebBindingInitializer设置上去的
ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
//conversionService来判断是否支持请求参数-->方法入参的转换,请求request中值都是String类型
//比如方法入参为String类型,返回的就是NoOpConvert,直接返回请求中的值
if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
try {
//底层通过工具类ConversionUtils的invokeConverter方法来执行
return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
}
catch (ConversionFailedException ex) {
conversionAttemptEx = ex;
}
}
}
..............代码略
总结,简单绘制了类型转换的时序图如下。这次记录比较粗糙,但是大体流程是这样,也方便自己查看以后,里面很多细节后续记录,比如Converter接口啊、转换工厂、转换适配器啊、Spring mvc:annotation-driven注册的118个转换器都可以转换哪些啊;