作者:杨绿寒轻 链接:https://juejin.cn/post/6844904177479450632 来源:稀土掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
源码解析
在了解完怎么样进行全局设置后,接下来我们通过debug源码来深入剖析一下Spring MVC是如何进行参数绑定的。
@RequestMapping("/date")
public DateEntity getDate(
LocalDate date,
LocalDateTime dateTime,
Date originalDate,
DateEntity dateEntity) {
System.out.printf("date=%s, dateTime=%s, originalDate=%s \n", date, dateTime, originalDate);
return dateEntity;
}
以下是收到请求后的方法调用栈的一些关键方法:
// DispatcherServlet处理请求
doService:943, DispatcherServlet
// 处理请求
doDispatch:1040, DispatcherServlet
// 生成调用链(前处理、实际调用方法、后处理)
handle:87, AbstractHandlerMethodAdapter
handleInternal:793, RequestMappingHandlerAdapter
// 反射获取到实际调用方法,准备开始调用
invokeHandlerMethod:879, RequestMappingHandlerAdapter
invokeAndHandle:105, ServletInvocableHandlerMethod
// 关键步骤,从这里开始处理请求参数
invokeForRequest:134, InvocableHandlerMethod
getMethodArgumentValues:167, InvocableHandlerMethod
resolveArgument:121, HandlerMethodArgumentResolverComposite
下面我们从关键的invokeForRequest:134, InvocableHandlerMethod
处开始分析,源码如下
// InvocableHandlerMethod.java
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 这里完成参数的转换,得到的是转换后的值
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
// 反射调用,真正开始执行方法
return doInvoke(args);
}
// 具体实现
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 获取当前handler method的方法参数数组,封装了入参信息,比如类型、泛型等
MethodParameter[] parameters = getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}
// 该数组用来存放从MethodParameter转换后的结果
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;
}
// resolvers是定义的成员变量,HandlerMethodArgumentResolverComposite类型,是各式各样的HandlerMethodArgumentResolver的集合。这里来判断一下是否存在支持当前方法参数的参数处理器
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
// 调用HandlerMethodArgumentResolverComposite来处理参数,下面会重点看一下内部的逻辑
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
......
}
}
return args;
}
下面需要进入HandlerMethodArgumentResolverComposite#resolveArgument
方法源码里面。
// HandlerMethodArgumentResolverComposite.java
@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
// 这里来获取匹配当前方法参数的参数解析器
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
if (resolver == null) {
throw new IllegalArgumentException("Unsupported parameter type [" +
parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
}
// 调用真正的参数解析器来处理参数并返回
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
// 获取匹配当前方法参数的参数解析器
@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
// 首先从缓存中查询是否有适配当前方法参数的参数解析器,首次进入是没有的
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
if (result == null) {
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
// 逐个遍历argumentResolvers这个list里的参数解析器来判断是否支持
if (resolver.supportsParameter(parameter)) {
result = resolver;
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}
复制代码
argumentResolvers
里一共有26个参数解析器,下面罗列一下常见的。
this.argumentResolvers = {LinkedList@6072} size = 26
0 = {RequestParamMethodArgumentResolver@6098}
1 = {RequestParamMapMethodArgumentResolver@6104}
2 = {PathVariableMethodArgumentResolver@6111}
3 = {PathVariableMapMethodArgumentResolver@6112}
......
7 = {RequestResponseBodyMethodProcessor@6116}
8 = {RequestPartMethodArgumentResolver@6117}
9 = {RequestHeaderMethodArgumentResolver@6118}
10 = {RequestHeaderMapMethodArgumentResolver@6119}
......
14 = {RequestAttributeMethodArgumentResolver@6123}
15 = {ServletRequestMethodArgumentResolver@6124}
......
24 = {RequestParamMethodArgumentResolver@6107}
25 = {ServletModelAttributeMethodProcessor@6133}
复制代码
所有的参数解析器都实现了HandlerMethodArgumentResolver
接口。
public interface HandlerMethodArgumentResolver {
// 上面用到用来判断当前参数解析器是否支持给定的方法参数
boolean supportsParameter(MethodParameter parameter);
// 解析给定的方法参数并返回
@Nullable
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}
到这里我们整理一下思路,对方法参数的解析都是通过逐个遍历找到合适的HandlerMethodArgumentResolver
来完成的。比如,如果参数上标注了@RequestParam
或者@RequestBody
或者@PathVariable
注解,SpringMVC会用不同的参数解析器来解析。下面挑一个最常用的RequestParamMethodArgumentResolver
来深入分析一下详细的解析流程。
RequestParamMethodArgumentResolver
继承自AbstractNamedValueMethodArgumentResolver
,AbstractNamedValueMethodArgumentResolver
实现了HandlerMethodArgumentResolver
接口的resolveArgument
方法。
// AbstractNamedValueMethodArgumentResolver.java
@Override
@Nullable
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
// 解析出传入的原始值,作为下面方法的参数
Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
......
if (binderFactory != null) {
// 创建 DataBinder
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
try {
// 通过DataBinder进行参数绑定,参数列表:原始值,目标类型,方法参数
arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
}
......
}
handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
return arg;
}
// DataBinder.java
@Override
@Nullable
public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
@Nullable MethodParameter methodParam) throws TypeMismatchException {
// 调用子类的convertIfNecessary方法,这里的具体实现是TypeConverterSupport
return getTypeConverter().convertIfNecessary(value, requiredType, methodParam);
}
// TypeConverterSupport.java
@Override
@Nullable
public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
@Nullable MethodParameter methodParam) throws TypeMismatchException {
// 调用重载的convertIfNecessary方法,通过MethodParameter构造了类型描述符TypeDescriptor
return convertIfNecessary(value, requiredType,
(methodParam != null ? new TypeDescriptor(methodParam) : TypeDescriptor.valueOf(requiredType)));
}
// convertIfNecessary方法
@Nullable
@Override
public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
@Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException {
Assert.state(this.typeConverterDelegate != null, "No TypeConverterDelegate");
try {
// 调用TypeConverterDelegate的convertIfNecessary方法
return this.typeConverterDelegate.convertIfNecessary(null, null, value, requiredType, typeDescriptor);
}
......
}
复制代码
接下来进入TypeConverterDelegate
的源码。
// TypeConverterDelegate.java
@Nullable
public <T> T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue,
@Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException {
// 查找是否有适合需求类型的自定义的PropertyEditor。还记得上面的 使用@ControllerAdvice配合@initBinder 那一节吗,如果有按那样配置,这里就会找到
PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
ConversionFailedException conversionAttemptEx = null;
// 查找到类型转换服务 ConversionService
ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
// 关键判断,如果没有PropertyEditor 就使用ConversionService
if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
try {
// #1,类型转换服务转换完成后就返回,下面会详细解释
return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
}
catch (ConversionFailedException ex) {
// fallback to default conversion logic below
conversionAttemptEx = ex;
}
}
}
Object convertedValue = newValue;
// 关键判断,如果有PropertyEditor就使用PropertyEditor
if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
......
// 由editor完成转换
convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
}
boolean standardConversion = false;
if (requiredType != null) {
// Try to apply some standard type conversion rules if appropriate.
if (convertedValue != null) {
if (Object.class == requiredType) {
return (T) convertedValue;
}
// 下面是数组、集合类型属性的处理,这里会遍历集合元素,递归调用convertIfNecessary转化,再收集处理结果
else if (requiredType.isArray()) {
// Array required -> apply appropriate conversion of elements.
if (convertedValue instanceof String && Enum.class.isAssignableFrom(requiredType.getComponentType())) {
convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
}
return (T) convertToTypedArray(convertedValue, propertyName, requiredType.getComponentType());
}
else if (convertedValue instanceof Collection) {
// Convert elements to target type, if determined.
convertedValue = convertToTypedCollection(
(Collection<?>) convertedValue, propertyName, requiredType, typeDescriptor);
standardConversion = true;
}
else if (convertedValue instanceof Map) {
// Convert keys and values to respective target type, if determined.
convertedValue = convertToTypedMap(
(Map<?, ?>) convertedValue, propertyName, requiredType, typeDescriptor);
standardConversion = true;
}
if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) {
convertedValue = Array.get(convertedValue, 0);
standardConversion = true;
}
if (String.class == requiredType && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) {
// We can stringify any primitive value...
return (T) convertedValue.toString();
}
else if (convertedValue instanceof String && !requiredType.isInstance(convertedValue)) {
......
}
else if (convertedValue instanceof Number && Number.class.isAssignableFrom(requiredType)) {
convertedValue = NumberUtils.convertNumberToTargetClass(
(Number) convertedValue, (Class<Number>) requiredType);
standardConversion = true;
}
}
else {
// convertedValue == null,空值处理
if (requiredType == Optional.class) {
convertedValue = Optional.empty();
}
}
......
}
// 异常处理
if (conversionAttemptEx != null) {
if (editor == null && !standardConversion && requiredType != null && Object.class != requiredType) {
throw conversionAttemptEx;
}
logger.debug("Original ConversionService attempt failed - ignored since " +
"PropertyEditor based conversion eventually succeeded", conversionAttemptEx);
}
return (T) convertedValue;
}
假如我们配置了自定义的Converter
,会进入#1的分支,由ConversionService
进行类型转换,以其子类GenericConversionService
为例。
// GenericConversionService.java
@Override
@Nullable
public Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {
......
// 从缓存中找到匹配类型的conveter,以LocalDateTime为例,会找到我们自定义的localDateTimeConverter
GenericConverter converter = getConverter(sourceType, targetType);
if (converter != null) {
// 通过工具方法调用真正的converter完成类型转换。至此,完成了源类型到目标类型的转换
Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType);
return handleResult(sourceType, targetType, result);
}
return handleConverterNotFound(source, sourceType, targetType);
}
以上就是处理标注@RequestParam
注解的参数的RequestParamMethodArgumentResolver
解析流程。
下面来看一下处理标注@RequestBody
注解的参数的RequestResponseBodyMethodProcessor
的解析流程,仍然是从resolveArgument
方法切入。
// RequestResponseBodyMethodProcessor.java
@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
parameter = parameter.nestedIfOptional();
// 在这里完成参数的解析
Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
......
return adaptArgumentIfNecessary(arg, parameter);
}
@Override
protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
Assert.state(servletRequest != null, "No HttpServletRequest");
ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
// 调用父类AbstractMessageConverterMethodArgumentResolver完成参数解析
Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
if (arg == null && checkRequired(parameter)) {
throw new HttpMessageNotReadableException("Required request body is missing: " +
parameter.getExecutable().toGenericString(), inputMessage);
}
return arg;
}
下面进入父类AbstractMessageConverterMethodArgumentResolver
的源码。
// AbstractMessageConverterMethodArgumentResolver.java
@Nullable
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
......
EmptyBodyCheckingHttpInputMessage message;
try {
message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
// 遍历HttpMessageConverter
for (HttpMessageConverter<?> converter : this.messageConverters) {
Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
GenericHttpMessageConverter<?> genericConverter =
(converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
(targetClass != null && converter.canRead(targetClass, contentType))) {
if (message.hasBody()) {
HttpInputMessage msgToUse =
getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
// 实际由MappingJackson2HttpMessageConverter调用父类AbstractJackson2HttpMessageConverter的read方法完成解析,
body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
}
else {
body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
}
break;
}
}
}
......
return body;
}
// AbstractJackson2HttpMessageConverter.java
@Override
public Object read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
// 获得要转换的目标参数Java类型,如LocalDateTime等
JavaType javaType = getJavaType(type, contextClass);
// 调用本类的readJavaType方法
return readJavaType(javaType, inputMessage);
}
// AbstractJackson2HttpMessageConverter.java
private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) throws IOException {
try {
if (inputMessage instanceof MappingJacksonInputMessage) {
Class<?> deserializationView = ((MappingJacksonInputMessage) inputMessage).getDeserializationView();
if (deserializationView != null) {
return this.objectMapper.readerWithView(deserializationView).forType(javaType).
readValue(inputMessage.getBody());
}
}
// 调用jackson类库,将HTTP的json请求信息解析为需要的参数类型。至此,将json请求转换成目标Java类型
return this.objectMapper.readValue(inputMessage.getBody(), javaType);
}
......
}
总结
controller
方法的参数是通过不同的HandlerMethodArgumentResolver
完成解析的。如果参数标注了@RequestBody
注解,实际上是通过MappingJackson2HttpMessageConverter
的ObjectMapper
将传入json格式数据反序列化解析成目标类型的。如果标注了@RequestParam
注解,是通过在应用初始化时注入到ConversionService
的一个个Converter
来实现的。其他的HandlerMethodArgumentResolver
也是各有各的用处,大家可以再看看相关代码,以便加深理解。