Spring提供了比较完善的参数解析器,虽然一直在使用,但是不知道Spring在后面做了什么样的处理,所以这次我们看一下Spring的源码,了解一下具体的处理过程。在本篇博客中我们使用@RequestParam来举例。
先看一下HandlerMethodArgumentResolver接口的定义:
public interface HandlerMethodArgumentResolver {
/**
* 解析器是否支持当前参数
*
* @param var1 需要被解析的Controller参数
* @return
*/
boolean supportsParameter(MethodParameter var1);
/**
* 将request中的请求参数解析到当前Controller参数上,在这里进行类型转换
*
* @param var1 需要被解析的Controller参数
* @param var2 当前request的ModelAndViewContainer
* @param var3 当前request
* @param var4 webDataBinderFactory 生成{@link WebDataBinderFactory}实例的工厂
* @return 解析转换后的参数
* @throws Exception
*/
Object resolveArgument(MethodParameter var1, ModelAndViewContainer var2, NativeWebRequest var3, WebDataBinderFactory var4) throws Exception;
}
再看一下HandlerMethodArgumentResolverComposite类(实现了HandlerMethodArgumentResolver接口):
public boolean supportsParameter(MethodParameter parameter) {
return this.getArgumentResolver(parameter) != null;
}
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;
}
通过分析上面的代码我们知道supportsParameter方法用于判断是否支持解析当前参数,getArgumentResolver方法用于获取参数解析器,而resolveArgument方法则负责寻找适合此参数的解析器进行解析。
首先SpringMVC在启动时会将所有的参数解析器放到HandlerMethodArgumentResolverComposite中,HandlerMethodArgumentResolverComposite是所有参数的一个集合,接下来就是从HandlerMethodArgumentResolverComposite参数解析器集合中选择一个支持对parameter解析的参数解析器,接下来就使用支持参数解析的解析器进行参数解析。这里使用的是责任链模式。
接下来我们再看一下AbstractNamedValueMethodArgumentResolver类(实现了HandlerMethodArgumentResolver接口):
public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
AbstractNamedValueMethodArgumentResolver.NamedValueInfo namedValueInfo = this.getNamedValueInfo(parameter);
MethodParameter nestedParameter = parameter.nestedIfOptional();
Object resolvedName = this.resolveStringValue(namedValueInfo.name);
if(resolvedName == null) {
throw new IllegalArgumentException("Specified name must not resolve to null: [" + namedValueInfo.name + "]");
} else {
Object arg = this.resolveName(resolvedName.toString(), nestedParameter, webRequest);
if(arg == null) {
if(namedValueInfo.defaultValue != null) {
arg = this.resolveStringValue(namedValueInfo.defaultValue);
} else if(namedValueInfo.required && !nestedParameter.isOptional()) {
this.handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
}
arg = this.handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
} else if("".equals(arg) && namedValueInfo.defaultValue != null) {
arg = this.resolveStringValue(namedValueInfo.defaultValue);
}
if(binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, (Object)null, namedValueInfo.name);
try {
arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
} catch (ConversionNotSupportedException var11) {
throw new MethodArgumentConversionNotSupportedException(arg, var11.getRequiredType(), namedValueInfo.name, parameter, var11.getCause());
} catch (TypeMismatchException var12) {
throw new MethodArgumentTypeMismatchException(arg, var12.getRequiredType(), namedValueInfo.name, parameter, var12.getCause());
}
}
this.handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
return arg;
}
}
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;
}
protected abstract AbstractNamedValueMethodArgumentResolver.NamedValueInfo createNamedValueInfo(MethodParameter var1);
protected abstract Object resolveName(String var1, MethodParameter var2, NativeWebRequest var3) throws Exception;
protected void handleMissingValue(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
this.handleMissingValue(name, parameter);
}
首先看一下几个需要注意的方法,resolveArgument方法用于转换参数,createNamedValueInfo方法用于获取参数注解的name和value信息,resolveName方法用于进行具体的参数转换,handleMissingValue方法则是当参数值为空且注解的默认值也为空时进行抛出异常的操作。
介绍这几个方法的原因是因为这几方法就是参数解析器进行参数解析时调用的主体方法,同时也是自定义参数解析器时需要自己实现的方法。
最后看一下@RequestParam注解的参数解析器RequestParamMethodArgumentResolver类(继承自AbstractNamedValueMethodArgumentResolver类):
public boolean supportsParameter(MethodParameter parameter) {
if(parameter.hasParameterAnnotation(RequestParam.class)) {
if(Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
String paramName = ((RequestParam)parameter.getParameterAnnotation(RequestParam.class)).name();
return StringUtils.hasText(paramName);
} else {
return true;
}
} else if(parameter.hasParameterAnnotation(RequestPart.class)) {
return false;
} else {
parameter = parameter.nestedIfOptional();
return MultipartResolutionDelegate.isMultipartArgument(parameter)?true:(this.useDefaultResolution?BeanUtils.isSimpleProperty(parameter.getNestedParameterType()):false);
}
}
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
RequestParam ann = (RequestParam)parameter.getParameterAnnotation(RequestParam.class);
return ann != null?new RequestParamMethodArgumentResolver.RequestParamNamedValueInfo(ann):new RequestParamMethodArgumentResolver.RequestParamNamedValueInfo();
}
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
HttpServletRequest servletRequest = (HttpServletRequest)request.getNativeRequest(HttpServletRequest.class);
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest)WebUtils.getNativeRequest(servletRequest, MultipartHttpServletRequest.class);
Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);
if(mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {
return mpArg;
} else {
Object arg = null;
if(multipartRequest != null) {
List paramValues = multipartRequest.getFiles(name);
if(!paramValues.isEmpty()) {
arg = paramValues.size() == 1?paramValues.get(0):paramValues;
}
}
if(arg == null) {
String[] paramValues1 = request.getParameterValues(name);
if(paramValues1 != null) {
arg = paramValues1.length == 1?paramValues1[0]:paramValues1;
}
}
return arg;
}
}
protected void handleMissingValue(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
HttpServletRequest servletRequest = (HttpServletRequest)request.getNativeRequest(HttpServletRequest.class);
if(MultipartResolutionDelegate.isMultipartArgument(parameter)) {
if(!MultipartResolutionDelegate.isMultipartRequest(servletRequest)) {
throw new MultipartException("Current request is not a multipart request");
} else {
throw new MissingServletRequestPartException(name);
}
} else {
throw new MissingServletRequestParameterException(name, parameter.getNestedParameterType().getSimpleName());
}
}
RequestParamMethodArgumentResolver类的主体代码和AbstractNamedValueMethodArgumentResolver类一致,执行流程也是一样的,不过RequestParamMethodArgumentResolver类是具体的参数解析器。
以上就是参数解析器涉及到的主要类、接口和主要的方法,具体的执行过程,大家自己调试一下就知道了。