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个转换器都可以转换哪些啊;

 

posted @ 2019-02-18 20:40  喜欢日向雏田一样的女子啊  阅读(1370)  评论(0编辑  收藏  举报