springMvc源码之@RequestParam参数绑定解析
2018-11-21 本文已影响0人
后来丶_a24d
问题点
- Http 调用接口的时候请求参数(String类型)由于有一个值为null, 而需要绑定的参数为Integer类型故一直404
- 在类型转时调用了Integer.valueOf(str), 而从底层源码来看当传入参数为空时会直接抛出throw new NumberFormatException("null");
解决方案
- 处理掉请求参数的null值并找到为null的原因。
源码简化版时序图
-
springmvc参数转换以及controller调用
springmvc参数转换以及controller调用.jpg -
springmvc绑定参数处理 从1.1开始
springmvc绑定参数处理.jpg
对标spring源码
- 从DispatchServlet的doService()开始分析
DispatcherServlet
- 在doService方法中调用doDispatch方法,handlerAdapter开始调用controller中对应方法。
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
RequestMappingHandlerAdapter
- 这个类是具体的处理url请求HandlerAdapter,其中调用到handleInternal()方法
- 继续调用invokeHandleMethod方法
return this.invokeHandleMethod(request, response, handlerMethod);
- 继续调用下一个类的invokeAndHandle()方法
ServletInvocableHandlerMethod[图片上传中...(springmvc绑定参数处理.jpg-7d2cfe-1548496536396-0)]
- 继承自InvocableHandlerMethod
- 查看对应的invokeAndHandle方法
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
//下一个调用口, 父类的方法
Object returnValue = this.invokeForRequest(webRequest, mavContainer, providedArgs);
this.setResponseStatus(webRequest);
if (returnValue == null) {
if (this.isRequestNotModified(webRequest) || this.hasResponseStatus() || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
} else if (StringUtils.hasText(this.responseReason)) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
try {
this.returnValueHandlers.handleReturnValue(returnValue, this.getReturnValueType(returnValue), mavContainer, webRequest);
} catch (Exception var6) {
if (this.logger.isTraceEnabled()) {
this.logger.trace(this.getReturnValueHandlingErrorMessage("Error handling return value", returnValue), var6);
}
throw var6;
}
}
InvocableHandlerMethod
- 上一个类调用父类的invokeForRequest方法, 在这个方法中解决完参数绑定问题之后调用对应方法。
public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//下一个调用的方法 解决参数绑定的形参
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
StringBuilder sb = new StringBuilder("Invoking [");
sb.append(getBeanType().getSimpleName()).append(".");
sb.append(getMethod().getName()).append("] method with arguments ");
sb.append(Arrays.asList(args));
logger.trace(sb.toString());
}
//反射调用
Object returnValue = doInvoke(args);
if (logger.isTraceEnabled()) {
logger.trace("Method [" + getMethod().getName() + "] returned [" + returnValue + "]");
}
return returnValue;
}
private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
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);
GenericTypeResolver.resolveParameterType(parameter, getBean().getClass());
args[i] = resolveProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
if (this.argumentResolvers.supportsParameter(parameter)) {
try {
//下一个类调用的方法
args[i] = this.argumentResolvers.resolveArgument(
parameter, mavContainer, request, this.dataBinderFactory);
continue;
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(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;
}
HandlerMethodArgumentResolverComposite
- 继承自HandlerMethodArgumentResolver
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
Assert.notNull(resolver, "Unknown parameter type [" + parameter.getParameterType().getName() + "]");
//下一个要调用的方法
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
AbstractNamedValueMethodArgumentResolver
- 继承自HandlerMethodArgumentResolver
- 用于从指定值解析方法参数的抽象基类. Request parameters, request headers, and path variables这些
public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
Class<?> paramType = parameter.getParameterType();
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
Object arg = resolveName(namedValueInfo.name, parameter, webRequest);
if (arg == null) {
if (namedValueInfo.defaultValue != null) {
arg = resolveDefaultValue(namedValueInfo.defaultValue);
}
else if (namedValueInfo.required && !parameter.getParameterType().getName().equals("java.util.Optional")) {
handleMissingValue(namedValueInfo.name, parameter);
}
arg = handleNullValue(namedValueInfo.name, arg, paramType);
}
else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
arg = resolveDefaultValue(namedValueInfo.defaultValue);
}
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
//下一个要调用的方法
arg = binder.convertIfNecessary(arg, paramType, parameter);
}
handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
return arg;
}
DataBinder
- implements PropertyEditorRegistry, TypeConverter
- 数据绑定,把HttpServletRequest中的数据纳入到Spring的管理。
- 将参数类型和请求数据再发给TypeConverter,由TypeConverter装配成一个bean。
- TypeConverter将装配好的bean返回给DataBinder
DataBinder将装配bean交给处理请求的方法。
public <T> T convertIfNecessary(Object value, Class<T> requiredType, MethodParameter methodParam)
throws TypeMismatchException {
return getTypeConverter().convertIfNecessary(value, requiredType, methodParam);
}
TypeConverterSupport
- 类型转换的实现,持有一个TypeConverterDelegate,具体转换工作交给TypeConverterDelegate完成.
- extends PropertyEditorRegistrySupport implements TypeConverter
public <T> T convertIfNecessary(Object value, Class<T> requiredType, MethodParameter methodParam)
throws TypeMismatchException {
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 {
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 (IllegalArgumentException ex) {
throw new TypeMismatchException(value, requiredType, ex);
}
}
TypeConverterDelegate
- 单独的类
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 {
Object convertedValue = newValue;
// Custom editor for this type?
PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
ConversionFailedException firstAttemptEx = null;
// No custom editor but custom ConversionService specified?
ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
if (editor == null && conversionService != null && convertedValue != null && typeDescriptor != null) {
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
TypeDescriptor targetTypeDesc = typeDescriptor;
if (conversionService.canConvert(sourceTypeDesc, targetTypeDesc)) {
try {
//下一个
return (T) conversionService.convert(convertedValue, sourceTypeDesc, targetTypeDesc);
}
catch (ConversionFailedException ex) {
// fallback to default conversion logic below
firstAttemptEx = ex;
}
}
}
。。。。
GenericConversionService
- implements ConfigurableConversionService
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
Assert.notNull(targetType, "targetType to convert to cannot be null");
if (sourceType == null) {
Assert.isTrue(source == null, "source must be [null] if sourceType == [null]");
return handleResult(null, targetType, convertNullSource(null, targetType));
}
if (source != null && !sourceType.getObjectType().isInstance(source)) {
throw new IllegalArgumentException("source to convert from must be an instance of " +
sourceType + "; instead it was a " + source.getClass().getName());
}
GenericConverter converter = getConverter(sourceType, targetType);
if (converter != null) {
//下一个
Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType);
return handleResult(sourceType, targetType, result);
}
return handleConverterNotFound(source, sourceType, targetType);
}
ConversionUtils
public static Object invokeConverter(GenericConverter converter, Object source, TypeDescriptor sourceType,
TypeDescriptor targetType) {
try {
return converter.convert(source, sourceType, targetType);
}
catch (ConversionFailedException ex) {
throw ex;
}
catch (Exception ex) {
throw new ConversionFailedException(sourceType, targetType, source, ex);
}
}
GenericConversionService的内部类ConverterFactoryAdapter中convert
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return convertNullSource(sourceType, targetType);
}
return this.converterFactory.getConverter(targetType.getObjectType()).convert(source);
}
StringToNumberConverterFactory的内部StringToNumberConverterFactory
- implements ConverterFactory<String, Number>
public <T extends Number> Converter<String, T> getConverter(Class<T> targetType) {
return new StringToNumber<T>(targetType);
}
public T convert(String source) {
if (source.length() == 0) {
return null;
}
return NumberUtils.parseNumber(source, this.targetType);
}
NumberUtils
public static <T extends Number> T parseNumber(String text, Class<T> targetClass) {
Assert.notNull(text, "Text must not be null");
Assert.notNull(targetClass, "Target class must not be null");
String trimmed = StringUtils.trimAllWhitespace(text);
if (targetClass.equals(Byte.class)) {
return (T) (isHexNumber(trimmed) ? Byte.decode(trimmed) : Byte.valueOf(trimmed));
}
else if (targetClass.equals(Short.class)) {
return (T) (isHexNumber(trimmed) ? Short.decode(trimmed) : Short.valueOf(trimmed));
}
else if (targetClass.equals(Integer.class)) {
return (T) (isHexNumber(trimmed) ? Integer.decode(trimmed) : Integer.valueOf(trimmed));
}
else if (targetClass.equals(Long.class)) {
return (T) (isHexNumber(trimmed) ? Long.decode(trimmed) : Long.valueOf(trimmed));
}
else if (targetClass.equals(BigInteger.class)) {
return (T) (isHexNumber(trimmed) ? decodeBigInteger(trimmed) : new BigInteger(trimmed));
}
else if (targetClass.equals(Float.class)) {
return (T) Float.valueOf(trimmed);
}
else if (targetClass.equals(Double.class)) {
return (T) Double.valueOf(trimmed);
}
else if (targetClass.equals(BigDecimal.class) || targetClass.equals(Number.class)) {
return (T) new BigDecimal(trimmed);
}
else {
throw new IllegalArgumentException(
"Cannot convert String [" + text + "] to target class [" + targetClass.getName() + "]");
}
}