该文SpringBoot版本:2.3.4
在DispatchServlet的doDispacth方法中,通过mappedHandler = getHandler(processedRequest);
获得请求应用的处理器(controller.method())和拦截器,但没有立刻执行处理器中对应的方法,而是通过HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
获得一个处理器的适配器,这个适配器主要是为了在原有处理器基础上,处理请求参数的返回值。这里面包含了大量的工作,下面主要来探究一下请求参数的处理。
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
容器中已经存在了一些适配器,循环遍历找到当前请求需要的。
其中调用adapter.supports(handler)
去判断是否是当前handler适配的。
我们常用的类是RequestMappingHandlerAdapter,对应用@RequestMapping标注过的方法。
选择它的判断方法则是
@Override
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
判断传进来的handler是否是HandlerMethod类型。
在获得HandlerAdapter后,在doDispatch()中调用mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
来执行方法。
一直追进来,最后InvocableHandlerMethod类中真正调用业务方法。
//InvocableHandlerMethod类
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
//调用方法
return doInvoke(args);
}
但在调用方法前,还需要处理参数getMethodArgumentValues
,做到请求参数和业务方法形参的匹配,已经相应类型的转换。
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//获得方法的形参的信息(参数名,参数类型,标注的注解...)
MethodParameter[] parameters = getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
//判断是否有合适的参数解析器
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
// Leave stack trace for later, exception may actually be resolved and handled...
if (logger.isDebugEnabled()) {
String exMsg = ex.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw ex;
}
}
return args;
}
@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
if (result == null) {
//循环判断容器中的参数解析器是否可以处理该参数
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
if (resolver.supportsParameter(parameter)) {
result = resolver;
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}
容器中已经存在了26个参数解析器
对应了不同的参数类型。
SpringMVC目标方法能写多少种参数类型,取决于参数解析器。
解析器中定义了两个方法,一个判断,一个处理。
举例来说PathVariableMethodArgumentResolver
是判断参数是否有@PathVariable
注解。
在确定参数解释器后,调用其中的resolveArgument
来解析参数
下面还是@PathVariable对应的解析器
@Override
@Nullable
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
MethodParameter nestedParameter = parameter.nestedIfOptional();
Object resolvedName = resolveStringValue(namedValueInfo.name);
if (resolvedName == null) {
throw new IllegalArgumentException(
"Specified name must not resolve to null: [" + namedValueInfo.name + "]");
}
//将参数值(现在还是字符串)从url中解析出来
Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
if (arg == null) {
if (namedValueInfo.defaultValue != null) {
arg = resolveStringValue(namedValueInfo.defaultValue);
}
else if (namedValueInfo.required && !nestedParameter.isOptional()) {
handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
}
arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
}
else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
arg = resolveStringValue(namedValueInfo.defaultValue);
}
if (binderFactory != null) {
//WebDataBinder :web数据绑定器,将请求参数的值绑定到指定类型的变量、javabean中
//其中会利用利用它里面的 Converters 将请求数据转成指定的数据类型
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
try {
//利用合适的数据转换器生成对应类型的对象并赋值
arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
}
catch (ConversionNotSupportedException ex) {
throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
namedValueInfo.name, parameter, ex.getCause());
}
catch (TypeMismatchException ex) {
throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
namedValueInfo.name, parameter, ex.getCause());
}
}
handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
//返回hanlder需要的已赋值的形参对象
return arg;
}
内置124个类型转换器,当然也可以自定义类型转换–>实现converter接口–>配置
@Configuration
public class MyConfig {
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer(){
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(自定义的converter);
}
}
}
就这样循环的获取每一个形参,最后传递给业务方法,调用doInvoke(args)
真正的执行业务方法。
在执行完Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
,就把参数解析和业务逻辑完成,后面就是返回值处理的过程。