参数解析
说到参数解析,springmvc中处理参数的是HandlerMethodArgumentResolver接口
public interface HandlerMethodArgumentResolver {
   // 判断是否支持该类型参数
   boolean supportsParameter(MethodParameter parameter);
   // 进行参数解析
   Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
         NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception;
}其有一个抽象实现类AbstractNamedValueMethodArgumentResolver,有很多有用的子类
- 
MapMethodProcessor 这个用来处理 Map/ModelMap 类型的参数,解析完成后返回 model。 
- 
PathVariableMethodArgumentResolver 
这个用来处理使用了 @PathVariable 注解并且参数类型不为 Map 的参数,参数类型为 Map 则使用 PathVariableMapMethodArgumentResolver 来处理。
- PathVariableMapMethodArgumentResolver
见上。
- ErrorsMethodArgumentResolver
这个用来处理 Error 参数,例如我们做参数校验时的 BindingResult。
- 
AbstractNamedValueMethodArgumentResolver 这个用来处理 key/value 类型的参数,如请求头参数、使用了 @PathVariable注解的参数以及 Cookie 等。
- 
RequestHeaderMethodArgumentResolver 
这个用来处理使用了 @RequestHeader 注解,并且参数类型不是 Map 的参数(参数类型是 Map 的使用 RequestHeaderMapMethodArgumentResolver)。
- RequestHeaderMapMethodArgumentResolver
见上。
- RequestAttributeMethodArgumentResolver
这个用来处理使用了 @RequestAttribute 注解的参数。
- RequestParamMethodArgumentResolver
这个功能就比较广了。使用了 @RequestParam 注解的参数、文件上传的类型 MultipartFile、或者一些没有使用任何注解的基本类型(Long、Integer)以及 String 等,都使用该参数解析器处理。需要注意的是,如果 @RequestParam 注解的参数类型是 Map,则该注解必须有 name 值,否则解析将由 RequestParamMapMethodArgumentResolver 完成。
- RequestParamMapMethodArgumentResolver
见上。
- AbstractCookieValueMethodArgumentResolver
这个是一个父类,处理使用了 @CookieValue 注解的参数。
- ServletCookieValueMethodArgumentResolver
这个处理使用了 @CookieValue 注解的参数。
- MatrixVariableMethodArgumentResolver
这个处理使用了 @MatrixVariable 注解并且参数类型不是 Map 的参数,如果参数类型是 Map,则使用 MatrixVariableMapMethodArgumentResolver 来处理。
- MatrixVariableMapMethodArgumentResolver
见上。
- SessionAttributeMethodArgumentResolver
这个用来处理使用了 @SessionAttribute 注解的参数。
- ExpressionValueMethodArgumentResolver
这个用来处理使用了 @Value 注解的参数。
- ServletResponseMethodArgumentResolver
这个用来处理 ServletResponse、OutputStream 以及 Writer 类型的参数。
- ModelMethodProcessor
这个用来处理 Model 类型参数,并返回 model。
- ModelAttributeMethodProcessor
这个用来处理使用了 @ModelAttribute 注解的参数。
- SessionStatusMethodArgumentResolver
这个用来处理 SessionStatus 类型的参数。
- PrincipalMethodArgumentResolver
这个用来处理 Principal 类型参数
- AbstractMessageConverterMethodArgumentResolver
这是一个父类,当使用 HttpMessageConverter 解析 requestbody 类型参数时,相关的处理类都会继承自它。
- RequestPartMethodArgumentResolver
这个用来处理使用了 @RequestPart 注解、MultipartFile 以及 Part 类型的参数。
- RequestResponseBodyMethodProcessor
这个用来处理添加了 @RequestBody 注解的参数。
- HttpEntityMethodProcessor
这个用来处理 HttpEntity 和 RequestEntity 类型的参数。
- ServletWebArgumentResolverAdapter
这个给父类提供 request。
- UriComponentsBuilderMethodArgumentResolver
这个用来处理 UriComponentsBuilder 类型的参数。
- ServletRequestMethodArgumentResolver
这个用来处理 WebRequest、ServletRequest、MultipartRequest、HttpSession、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId 类型的参数。
- HandlerMethodArgumentResolverComposite
这个看名字就知道是一个组合解析器,它是一个代理,具体代理其他干活的那些参数解析器。
- RedirectAttributesMethodArgumentResolver
这个用来处理 RedirectAttributes 类型的参数
这些解析器是在执行
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());执行对应的Controller方法前来进行参数解析时调用的org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues
if (this.argumentResolvers.supportsParameter(parameter)) {
   try {
      args[i] = this.argumentResolvers.resolveArgument(
            parameter, mavContainer, request, this.dataBinderFactory);
      continue;
   }
   catch (Exception ex) {
      if (logger.isDebugEnabled()) {
         logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex);
      }
      throw ex;
   }
}RequestResponseBodyMethodProcessor调用消息解析器
我之前只知道解析@RequestBody需要使用消息解析器HttpMessageConverter,但是没有深究是从哪调用的。突然看到消息解析我才知道原来是RequestResponseBodyMethodProcessor调用的。org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#resolveArgument来进行参数解析
遍历消息解析器找到可以进行解析该类型的消息解析器
for (HttpMessageConverter<?> converter : this.messageConverters) {对于简单的参数会以简单的转换器进行转换,而这些简单的转换器是Spring MVC自身已经提供了的,但是如果是转换HTTP请求体,会调用HttpMessageConverter接口的方法对请求体的信息进行转换
public interface HttpMessageConverter<T> {
   
  // 是否可读,clazz为java类型,mediaType为HTTP请求类型
   boolean canRead(Class<?> clazz, MediaType mediaType);
   
  // 判断clazz类型是否能够转换为mediaType媒体类型,其中clazz为java类型,mediaType为HTTP响应类型
   boolean canWrite(Class<?> clazz, MediaType mediaType);
   
  // 可支持的媒体类型列表
   List<MediaType> getSupportedMediaTypes();
   
  // 当canRead验证通过后,读入HTTP请求信息
   T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
         throws IOException, HttpMessageNotReadableException;
   
  // 单canWrite方法验证通过后,写入响应
   void write(T t, MediaType contentType, HttpOutputMessage outputMessage)
         throws IOException, HttpMessageNotWritableException;
}HttpMessageConverter接口是将HTTP请求体转换为对应的java对象,对于HTTP参数和其他内容需要使用参数转换规则
参数转换
Spring MVC中,是通过WebDataBinder机制来获取参数的,它的主要作用是解析HTTP请求的上下文,然后在控制器的调用之前转换参数并且提供验证的功能,为调用控制器方法做准备。处理器会从HTTP请求中读取数据,然后通过三种接口进行各类参数转换(Converter、Formatter、GenericConverter)。
Converter接口
Converter接口是一个普通的转换器
public interface Converter<S, T> {
 
 T convert(S source);
}可以将某个类型转换为另一个类型
Formatter接口
Formatter接口是一个格式化转换器,如将日期字符串格式化
public interface Formatter<T> extends Printer<T>, Parser<T> {
}GenericConverter接口
GenericConverter接口是将HTTP参数转换为数组
public interface GenericConverter {
   
   Set<ConvertiblePair> getConvertibleTypes();
   
   Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
   /**
    * Holder for a source-to-target class pair.
    */
   final class ConvertiblePair {
      private final Class<?> sourceType;
      private final Class<?> targetType;
      /**
       * Create a new source-to-target pair.
       * @param sourceType the source type
       * @param targetType the target type
       */
      public ConvertiblePair(Class<?> sourceType, Class<?> targetType) {
         Assert.notNull(sourceType, "Source type must not be null");
         Assert.notNull(targetType, "Target type must not be null");
         this.sourceType = sourceType;
         this.targetType = targetType;
      }
      public Class<?> getSourceType() {
         return this.sourceType;
      }
      public Class<?> getTargetType() {
         return this.targetType;
      }
      @Override
      public boolean equals(Object other) {
         if (this == other) {
            return true;
         }
         if (other == null || other.getClass() != ConvertiblePair.class) {
            return false;
         }
         ConvertiblePair otherPair = (ConvertiblePair) other;
         return (this.sourceType == otherPair.sourceType && this.targetType == otherPair.targetType);
      }
      @Override
      public int hashCode() {
         return (this.sourceType.hashCode() * 31 + this.targetType.hashCode());
      }
      @Override
      public String toString() {
         return (this.sourceType.getName() + " -> " + this.targetType.getName());
      }
   }
}ConversionService接口
public interface ConversionService {
   boolean canConvert(Class<?> sourceType, Class<?> targetType);
   boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);
   <T> T convert(Object source, Class<T> targetType);
   Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}数据验证
Validator接口用于数据验证
public interface Validator {
  // 判断当前验证器是否支持该Class类型的验证
   boolean supports(Class<?> clazz);
   
  // 如果supports返回true,则执行该方法验证逻辑
   void validate(Object target, Errors errors);
}WebDataBinder还可以进行验证,使用@InitBinder注解可以允许在进入控制器方法之前修改WebDataBinder机制,可以来设置验证器
通过WebDataBinder#setValidator来添加验证器
本文由mdnice多平台发布