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时,可以使用参数名获取的愿意。