天天看点

spring源码解析-web系列(五):解析请求参数

spring源码解析-web系列(一):启动

spring源码解析-web系列(二):处理请求的过程

spring源码解析-web系列(三):九大组件之HandlerMapping

spring源码解析-web系列(四):九大组件之HandlerAdapter

spring源码解析-web系列(五):解析请求参数

spring源码解析-web系列(六):九大组件之ViewResolver

spring源码解析-web系列(七):九大组件之HandlerExceptionResolver

转载请标明出处:

https://blog.csdn.net/bingospunky/article/details/98620658

本文出自马彬彬的博客

参数的分类

我们在使用spring-web时,配置参数的形式有很多,比如:@PathVariable、@RequestParam、@SessionAttribute、@ModelAttribute、Model。

本文把他们分为两类,根据在源码里被处理的位置划分:1.在InvocableHandlerMethod中使用HandlerMethodArgumentResolver处理的的,比如@PathVariable、@RequestParam。2.不是HandlerMethodArgumentResolver处理的,比如@ModelAttribute、@SessionAttribute、对FlashMap的处理,这些参数一般是在RequestMappingHandlerAdapter.invokeHandleMethod方法中。

本文只叙述HandlerMethodArgumentResolver解析参数的处理。后面一类参数用的比较少。

解析参数的过程

参数是在InvocableHandlerMethod的getMethodArgumentValues方法被解析的。

代码1 (org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues):

private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
        MethodParameter[] parameters = this.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, this.getBean().getClass());
            args[i] = this.resolveProvidedArgument(parameter, providedArgs);
            if (args[i] == null) {
                if (this.argumentResolvers.supportsParameter(parameter)) {
                    try {
                        args[i] = this.argumentResolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
                    } catch (Exception var9) {
                        if (this.logger.isDebugEnabled()) {
                            this.logger.debug(this.getArgumentResolutionErrorMessage("Error resolving argument", i), var9);
                        }

                        throw var9;
                    }
                } else if (args[i] == null) {
                    String msg = this.getArgumentResolutionErrorMessage("No suitable resolver for argument", i);
                    throw new IllegalStateException(msg);
                }
            }
        }

        return args;
    }
           

代码1第2行获取所有的MethodParameter。MethodParameter是什么呢?MethodParameter是对Controller一个方法里的一个参数的描述,包含Method信息、参数Index、参数的注解等信息。这些MethodParameter是在初始化时扫描所有的Controller时加载的。

代码1遍历获取到的MethodParameter,分别获取真实的参数。代码1第9行providedArgs为null,所以通过代码1第13行的代码获取真实参数。代码1第21~24行,获取不到真实参数时,抛异常。

获取真实参数的代码如下:

代码2 (org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument):

public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        HandlerMethodArgumentResolver resolver = this.getArgumentResolver(parameter);
        if (resolver == null) {
            throw new IllegalArgumentException("Unknown parameter type [" + parameter.getParameterType().getName() + "]");
        } else {
            return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
        }
    }
    private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
        HandlerMethodArgumentResolver result = (HandlerMethodArgumentResolver)this.argumentResolverCache.get(parameter);
        if (result == null) {
            Iterator var3 = this.argumentResolvers.iterator();

            while(var3.hasNext()) {
                HandlerMethodArgumentResolver methodArgumentResolver = (HandlerMethodArgumentResolver)var3.next();
                if (this.logger.isTraceEnabled()) {
                    this.logger.trace("Testing if argument resolver [" + methodArgumentResolver + "] supports [" + parameter.getGenericParameterType() + "]");
                }

                if (methodArgumentResolver.supportsParameter(parameter)) {
                    result = methodArgumentResolver;
                    this.argumentResolverCache.put(parameter, methodArgumentResolver);
                    break;
                }
            }
        }

        return result;
    }
           

代码2第2行先获取解析这个参数的HandlerMethodArgumentResolver。获取的过程在getArgumentResolver方法中,先在缓存查找,如果没有,则代码2第12行遍历this.argumentResolvers数组,返回支持处理这个参数的HandlerMethodArgumentResolver。那么这些解析器的来源在哪里呢?

HandlerMethodArgumentResolver的来源

1.argumentResolvers是HandlerMethodArgumentResolverComposite对象的属性,这个HandlerMethodArgumentResolverComposite对象是InvocableHandlerMethod里的argumentResolvers属性,InvocableHandlerMethod的argumentResolvers属性是在如下位置被赋值。

代码3 (org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod):

ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
	invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
           

代码3里RequestMappingHandlerAdapter处理每个请求时,都会创建ServletInvocableHandlerMethod,然后把RequestMappingHandlerAdapter的this.argumentResolvers属性赋值给ServletInvocableHandlerMethod。

那么RequestMappingHandlerAdapter里的this.argumentResolvers是如何获取的呢?在上一篇博客介绍

RequestMappingHandlerAdapter加载过程的时候,在afterPropertiesSet方法初始化,会创建argumentResolvers对象并添加默认的HandlerMethodArgumentResolver,代码如下:

代码4 (org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.getDefaultArgumentResolvers):

private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
        List<HandlerMethodArgumentResolver> resolvers = new ArrayList();
        resolvers.add(new RequestParamMethodArgumentResolver(this.getBeanFactory(), false));
        resolvers.add(new RequestParamMapMethodArgumentResolver());
        resolvers.add(new PathVariableMethodArgumentResolver());
        resolvers.add(new PathVariableMapMethodArgumentResolver());
        resolvers.add(new MatrixVariableMethodArgumentResolver());
        resolvers.add(new MatrixVariableMapMethodArgumentResolver());
        resolvers.add(new ServletModelAttributeMethodProcessor(false));
        resolvers.add(new RequestResponseBodyMethodProcessor(this.getMessageConverters()));
        resolvers.add(new RequestPartMethodArgumentResolver(this.getMessageConverters()));
        resolvers.add(new RequestHeaderMethodArgumentResolver(this.getBeanFactory()));
        resolvers.add(new RequestHeaderMapMethodArgumentResolver());
        resolvers.add(new ServletCookieValueMethodArgumentResolver(this.getBeanFactory()));
        resolvers.add(new ExpressionValueMethodArgumentResolver(this.getBeanFactory()));
        resolvers.add(new ServletRequestMethodArgumentResolver());
        resolvers.add(new ServletResponseMethodArgumentResolver());
        resolvers.add(new HttpEntityMethodProcessor(this.getMessageConverters()));
        resolvers.add(new RedirectAttributesMethodArgumentResolver());
        resolvers.add(new ModelMethodProcessor());
        resolvers.add(new MapMethodProcessor());
        resolvers.add(new ErrorsMethodArgumentResolver());
        resolvers.add(new SessionStatusMethodArgumentResolver());
        resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
        if (this.getCustomArgumentResolvers() != null) {
            resolvers.addAll(this.getCustomArgumentResolvers());
        }

        resolvers.add(new RequestParamMethodArgumentResolver(this.getBeanFactory(), true));
        resolvers.add(new ServletModelAttributeMethodProcessor(true));
        return resolvers;
    }
           

至此,HandlerMethodArgumentResolver的来源就搞清楚了。总结一下:RequestMappingHandlerAdapter初始化的时候创建了一个HandlerMethodArgumentResolverComposite对象并添加默认的HandlerMethodArgumentResolver,且RequestMappingHandlerAdapter在BeanFactory里是单例的,所以HandlerMethodArgumentResolverComposite对象也是单例的。在处理每个请求时,都会创建ServletInvocableHandlerMethod,然后把单例的HandlerMethodArgumentResolverComposite对象赋值给每个ServletInvocableHandlerMethod使用。

HandlerMethodArgumentResolver

HandlerMethodArgumentResolver接口就是用来解析参数的,只定义了两个方法:1.判断该Resolver使用可以解析某个参数。2.具体解析参数。

代码5 (org.springframework.web.method.support.HandlerMethodArgumentResolver):

public interface HandlerMethodArgumentResolver {
    boolean supportsParameter(MethodParameter var1);
    Object resolveArgument(MethodParameter var1, ModelAndViewContainer var2, NativeWebRequest var3, WebDataBinderFactory var4) throws Exception;
}
           

在代码4中可以发现spring已经实现了很多的Resolver,这里挑两个有代表性的来叙述。

ModelMethodProcessor

代码6 (org.springframework.web.method.annotation.ModelMethodProcessor):

public boolean supportsParameter(MethodParameter parameter) {
        return Model.class.isAssignableFrom(parameter.getParameterType());
    }

    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        return mavContainer.getModel();
    }
           

ModelMethodProcessor的实现很简单,判断是否支持时根据参数的类型,解析参数时直接获取mavContainer力的model的值。

PathVariableMethodArgumentResolver

PathVariableMethodArgumentResolver继承AbstractNamedValueMethodArgumentResolver,AbstractNamedValueMethodArgumentResolver实现HandlerMethodArgumentResolver接口。

PathVariableMethodArgumentResolver的supportsParameter方法比较简单,主要就是根据参数上时是否被@PathVariable注解来判断的。

resolveArgument方法被AbstractNamedValueMethodArgumentResolver实现,然后提供了一些模板方法供PathVariableMethodArgumentResolver实现。

代码7 (org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.resolveArgument):

public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        Class<?> paramType = parameter.getParameterType();
        AbstractNamedValueMethodArgumentResolver.NamedValueInfo namedValueInfo = this.getNamedValueInfo(parameter);
        Object arg = this.resolveName(namedValueInfo.name, parameter, webRequest);
        if (arg == null) {
            if (namedValueInfo.defaultValue != null) {
                arg = this.resolveDefaultValue(namedValueInfo.defaultValue);
            } else if (namedValueInfo.required && !parameter.getParameterType().getName().equals("java.util.Optional")) {
                this.handleMissingValue(namedValueInfo.name, parameter);
            }

            arg = this.handleNullValue(namedValueInfo.name, arg, paramType);
        } else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
            arg = this.resolveDefaultValue(namedValueInfo.defaultValue);
        }

        if (binderFactory != null) {
            WebDataBinder binder = binderFactory.createBinder(webRequest, (Object)null, namedValueInfo.name);

            try {
                arg = binder.convertIfNecessary(arg, paramType, parameter);
            } catch (ConversionNotSupportedException var10) {
                throw new MethodArgumentConversionNotSupportedException(arg, var10.getRequiredType(), namedValueInfo.name, parameter, var10.getCause());
            } catch (TypeMismatchException var11) {
                throw new MethodArgumentTypeMismatchException(arg, var11.getRequiredType(), namedValueInfo.name, parameter, var11.getCause());
            }
        }

        this.handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
        return arg;
    }
           

代码8 (org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.NamedValueInfo):

protected static class NamedValueInfo {
        private final String name;
        private final boolean required;
        private final String defaultValue;

        public NamedValueInfo(String name, boolean required, String defaultValue) {
            this.name = name;
            this.required = required;
            this.defaultValue = defaultValue;
        }
    }
           

代码9 (org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver.resolveName):

protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
		Map<String, String> uriTemplateVars = (Map<String, String>) request.getAttribute(
				HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
		return (uriTemplateVars != null ? uriTemplateVars.get(name) : null);
	}
           

代码7第3行,创建了NamedValueInfo对象,NamedValueInfo包含name、required、defalultValue等属性,如代码8所示。代码7第4行通过resolveName方法获取真实的参数,resolveName方法由子类实现,如代码9所示,在uriTemplateVars里查找,uriTemplateVars是HandlerMapping根据lookupPath找到处理请求的处理器后设置的。代码7第6第12行使用模板方法处理参数为空的情况,比如代码7第9行的方法直接抛出***MissingPathVariableException***异常。代码7第17第27行通过binderFactory对参数进行转换。代码7第29行调用handleResolvedValue处理获取到的参数,在PathVariableMethodArgumentResolver中的实现就是把相关信息缓存在HTTPServletRequest中。

下面我们来看一下代码7第3行获取NamedValueInfo的过程。

代码10 (org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver):

private AbstractNamedValueMethodArgumentResolver.NamedValueInfo getNamedValueInfo(MethodParameter parameter) {
        AbstractNamedValueMethodArgumentResolver.NamedValueInfo namedValueInfo = (AbstractNamedValueMethodArgumentResolver.NamedValueInfo)this.namedValueInfoCache.get(parameter);
        if (namedValueInfo == null) {
            namedValueInfo = this.createNamedValueInfo(parameter);
            namedValueInfo = this.updateNamedValueInfo(parameter, namedValueInfo);
            this.namedValueInfoCache.put(parameter, namedValueInfo);
        }

        return namedValueInfo;
    }
    
    private AbstractNamedValueMethodArgumentResolver.NamedValueInfo updateNamedValueInfo(MethodParameter parameter, AbstractNamedValueMethodArgumentResolver.NamedValueInfo info) {
        String name = info.name;
        if (info.name.length() == 0) {
            name = parameter.getParameterName();
            if (name == null) {
                throw new IllegalArgumentException("Name for argument type [" + parameter.getParameterType().getName() + "] not available, and parameter name information not found in class file either.");
            }
        }

        String defaultValue = "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n".equals(info.defaultValue) ? null : info.defaultValue;
        return new AbstractNamedValueMethodArgumentResolver.NamedValueInfo(name, info.required, defaultValue);
    }
           

代码11 (org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver.createNamedValueInfo):

@Override
	protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
		PathVariable annotation = parameter.getParameterAnnotation(PathVariable.class);
		return new PathVariableNamedValueInfo(annotation);
	}
	private static class PathVariableNamedValueInfo extends NamedValueInfo {
		public PathVariableNamedValueInfo(PathVariable annotation) {
			super(annotation.value(), true, ValueConstants.DEFAULT_NONE);
		}
	}
           

代码10第4行调用createNamedValueInfo方法创建NamedValueInfo,createNamedValueInfo由子类实现,在PathVariableMethodArgumentResolver中的实现如代码11所示,通过注解获取name,required设置为true,defaultValue设置为ValueConstants.DEFAULT_NONE。代码10第5行调用updateNamedValueInfo方法更新NamedValueInfo。更新的过程在如代码10第12~第23行所示,如果name长度为0,则通过代码10第15行的***parameter.getParameterName()***重新获取name;代码10第21行根据条件对默认值进行替换。重新生成NamedValueInfo。

代码10第15行的***parameter.getParameterName()***重新获取name的方法如下:

代码12 (org.springframework.core.MethodParameter.getParameterName):

public String getParameterName() {
		ParameterNameDiscoverer discoverer = this.parameterNameDiscoverer;
		if (discoverer != null) {
			String[] parameterNames = (this.method != null ? discoverer.getParameterNames(this.method) : discoverer.getParameterNames(this.constructor));
			if (parameterNames != null) {
				this.parameterName = parameterNames[this.parameterIndex];
			}
			this.parameterNameDiscoverer = null;
		}
		return this.parameterName;
	}
           

代码12第45行获取该方法的所有参数名称,然后通过索引获取当前参数的名字。获取方法的所有参数名称使用的是discoverer对象,它是ParameterNameDiscoverer类型的。那么这个对象是从哪里来的呢?discoverer的来源和本文上面提到的 ***HandlerMethodArgumentResolver的来源***相似,它也是RequestMappingHandlerAdapter创建的对象,然后一层层传递到这里使用的。在RequestMappingHandlerAdapter中创建的代码如下: private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer(); ,可以看到这个discoverer是DefaultParameterNameDiscoverer类型的。

DefaultParameterNameDiscoverer代码如下:

代码13 (org.springframework.core.DefaultParameterNameDiscoverer):

public class DefaultParameterNameDiscoverer extends PrioritizedParameterNameDiscoverer {

	private static final boolean standardReflectionAvailable = ClassUtils.isPresent(
			"java.lang.reflect.Executable", DefaultParameterNameDiscoverer.class.getClassLoader());

	public DefaultParameterNameDiscoverer() {
		if (standardReflectionAvailable) {
			addDiscoverer(new StandardReflectionParameterNameDiscoverer());
		}
		addDiscoverer(new LocalVariableTableParameterNameDiscoverer());
	}
}
           

这个DefaultParameterNameDiscoverer很符合spring的风格,它自己不干活,它包含了StandardReflectionParameterNameDiscoverer和LocalVariableTableParameterNameDiscoverer,当需要获取方法的参数名称时,依次调用这两个Discoverer获取参数名称。

StandardReflectionParameterNameDiscoverer是通过反射获取参数名的,LocalVariableTableParameterNameDiscoverer是通过asm框架获取参数名的。这就是当我们使用@PathVariable、@RequestParam等注解时,如果不指定name时,可以使用参数名获取的愿意。