SpringBoot源码系列(5):参数解析

布鸽不鸽 Lv4

前言

在SpringBoot/SpringMVC中,我们能在Controller中解析出请求的参数,本文谈一谈其背后的原理。本文中SpringBoot版本号为2.7.5。

原文地址:https://xuedongyun.cn/post/13517/

参数解析原理

DispatcherServlet

之前的文章已经提到,DispatcherServlet的核心方法是doDispatch。在doDispatch方法中,我们先通过HandlerMappings找到了handler。再为当前Handler找到HandlerAdapter

这一部分可以查看我之前的博文复习:SpringBoot源码系列(4):请求映射

1
2
mappedHandler = getHandler(processedRequest);
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

getHandlerAdapter方法,遍历this.handlerAdapters,来找到合适的HandlerAdapter

1
2
3
4
5
6
7
8
9
10
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("...");
}

其中this.handlerAdapters包含了四个,完成不同的功能

1
2
3
4
5
this.handlerAdapters = {ArrayList} size=4
0 = (RequestMappingHandlerAdapter) // 支持标注@RequestMapping
1 = (HandlerFunctionAdapter) // 支持函数式编程
2 = {HttpRequestHandlerAdapter}
3 = {SimpleControllerHandlerAdapter}

RequestMappingHandlerAdapter

通过步入可以看到,RequestMappingHandlerAdapter(其父类AbstractHandlerMethodAdapter)实现了supports方法。

这里主要进行了类型判断,看handler是否是HandlerMethod类型。(RequestMappingHandlerAdaptersupportsInternal总返回true)

1
2
3
4
@Override
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}

DispatcherServlet

现在回到DispatcherServlet中,开始使用handlerAdapter真正执行handler方法

1
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

RequestMappingHandlerAdapter

通过步入可以看到,RequestMappingHandlerAdapter(其父类AbstractHandlerMethodAdapter)实现了handle方法。

1
2
3
4
5
6
7
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {

return handleInternal(request, response, (HandlerMethod) handler);
}

RequestMappingHandlerAdapter实现了handleInternal方法。如果设置synchronizeOnSession为true,那么对于同一个session,需要加锁。可以看到,核心方法为invokeHandlerMethod

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 代码有删改
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

ModelAndView mav;
checkRequest(request);

// 同一个session加锁
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
// 加锁
} else {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
} else {
mav = invokeHandlerMethod(request, response, handlerMethod);
}

return mav;
}

invokeHandlerMethod方法。其核心在于请求值解析器和返回值处理器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 代码有删改
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
// argumentResolvers:请求值解析器,用于确定目标方法每一个参数值是什么
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
// returnValueHandlers:返回值处理器
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}

ModelAndViewContainer mavContainer = new ModelAndViewContainer();

// 执行并处理
invocableMethod.invokeAndHandle(webRequest, mavContainer);

return getModelAndView(mavContainer, modelFactory, webRequest);
}
}
1
2
3
4
5
6
7
8
9
10
11
// 请求值解析器
argumentResolvers = {ArrayList} size = 27
0 = {RequestParamMethodArgumentResolver}
1 = {RequestParamMapMethodArgumentResolver}
2 = {PathVariableMethodArgumentResolver}
3 = {PathVariableMapMethodArgumentResolver}
4 = {MatrixVariableMethodArgumentResolver}
5 = {MatrixVariableMapMethodArgumentResolver}
6 = {ServletModelAttributeMethodProcessor
7 = {RequestResponseBodyMethodProcessor}
...
1
2
3
4
5
6
7
8
// 返回值处理器
returnValueHandlers = {ArrayList} size = 15
0 = {ModelAndViewMethodReturnValueHandler}
1 = {ModelMethodProcessor)
2 = {ViewMethodReturnValueHandler}
3 = {ResponseBodyEmitterReturnValueHandler}
4 = {StreamingResponseBodyReturnValueHandler}
...

HandlerMethodArgumentResolver

我们可以简单看一下请求值解析器的接口设计。其中supportsParameter方法用于判断解析器是否支持解析当前参数。若支持,则将调用resolveArgument方法进行解析

1
2
3
4
5
6
7
8
9
public interface HandlerMethodArgumentResolver {

boolean supportsParameter(MethodParameter parameter);

@Nullable
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;

}

ServletInvocableHandlerMethod

RequestMappingHandlerAdapter中,调用了invocableMethod.invokeAndHandle。我们现在查看ServletInvocableHandlerMethodinvokeAndHandle方法

1
2
3
4
5
6
7
// 代码有删改
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {

Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
}

继续查看invokeForRequest方法。可以看到,使用了getMethodArgumentValues方法获取了所有请求参数的值。最终调用doInvoke方法,利用反射执行了我们Controller中的方法。

1
2
3
4
5
6
7
8
9
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {

// 获取所有参数的值
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);

return doInvoke(args);
}

我们查看getMethodArgumentValues方法,该方法获取请求参数的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 代码有删改
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);
}
}
// 返回目标函数参数的值
return args;
}

HandlerMethodArgumentResolverComposite

我们之前调用了this.resolvers.supportsParameter(parameter)用于判断参数解析器是否支持当前参数。现在来看一下其中的源码

1
2
3
4
5
@Override
public boolean supportsParameter(MethodParameter parameter) {
// 拿到参数解析器
return getArgumentResolver(parameter) != null;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@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;
}

我们可以看一些例子,明白resolver.supportsParameter背后是如何判断的

比如(第一个)RequestParamMethodArgumentResolver解析器:参数要标@RequestParam注解

1
if (parameter.hasParameterAnnotation(RequestParam.class)) {}

比如(第二个)RequestParamMapMethodArgumentResolver解析器:参数要标@RequestParam注解,参数要为Map类型,@RequestParam注解中要没有name

1
2
3
4
RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
return (requestParam != null &&
Map.class.isAssignableFrom(parameter.getParameterType()) &&
!StringUtils.hasText(requestParam.name()));

而后,this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory)用于解析参数,我们看一看其中源码

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

// 拿到当前参数的参数解析器(上面已经缓存好了)
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
if (resolver == null) {
throw new IllegalArgumentException("...");
}
// 核心代码
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}

HandlerMethodArgumentResolver

可以看到,参数解析靠的还是参数解析器的resolveArgument方法(所有参数解析器都实现了HandlerMethodArgumentResolver接口,我们上面也谈过)。

(举例一)路径变量

我们这里以PathVariable参数为例来看一看背后的原理。其中类继承关系:HandlerMethodArgumentResolver <- AbstractNamedValueMethodArgumentResolver <- PathVariableMethodArgumentResolver

首先,PathVariableMethodArgumentResolver实现了supportsParameter方法。参数要标@PathVariable注解,若参数为Map类型,@PathVariable注解中要没有value

1
2
3
4
5
6
7
8
9
10
11
@Override
public boolean supportsParameter(MethodParameter parameter) {
if (!parameter.hasParameterAnnotation(PathVariable.class)) {
return false;
}
if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
PathVariable pathVariable = parameter.getParameterAnnotation(PathVariable.class);
return (pathVariable != null && StringUtils.hasText(pathVariable.value()));
}
return true;
}

其次,AbstractNamedValueMethodArgumentResolver实现resolveArgument方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@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();

// 为了处理注解中使用了SpEL表达式的情况,不用管
Object resolvedName = resolveEmbeddedValuesAndExpressions(namedValueInfo.name);

// 解析参数的值,核心
Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);

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

PathVariableMethodArgumentResolver实现了resolveName方法

1
2
3
4
5
6
7
8
9
10
@Override
@SuppressWarnings("unchecked")
@Nullable
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);

// uriTemplateVars存储路径变量的值,直接获取
return (uriTemplateVars != null ? uriTemplateVars.get(name) : null);
}

(举例二)Servlet API

我们这里以Servlet API参数为例来看一看背后的原理。其中类继承关系:HandlerMethodArgumentResolver <- ServletRequestMethodArgumentResolver

首先,ServletRequestMethodArgumentResolver实现了supportsParameter方法。参数需要为特定的类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public boolean supportsParameter(MethodParameter parameter) {
Class<?> paramType = parameter.getParameterType();
return (WebRequest.class.isAssignableFrom(paramType) ||
ServletRequest.class.isAssignableFrom(paramType) ||
MultipartRequest.class.isAssignableFrom(paramType) ||
HttpSession.class.isAssignableFrom(paramType) ||
(pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) ||
(Principal.class.isAssignableFrom(paramType) && !parameter.hasParameterAnnotations()) ||
InputStream.class.isAssignableFrom(paramType) ||
Reader.class.isAssignableFrom(paramType) ||
HttpMethod.class == paramType ||
Locale.class == paramType ||
TimeZone.class == paramType ||
ZoneId.class == paramType);
}

其次,ServletRequestMethodArgumentResolver实现了resolveArgument方法。根据具体类型执行不同的操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

Class<?> paramType = parameter.getParameterType();

// WebRequest / NativeWebRequest / ServletWebRequest
if (WebRequest.class.isAssignableFrom(paramType)) {
if (!paramType.isInstance(webRequest)) {
throw new IllegalStateException("...");
}
return webRequest;
}

// ServletRequest / HttpServletRequest / MultipartRequest / MultipartHttpServletRequest
if (ServletRequest.class.isAssignableFrom(paramType) || MultipartRequest.class.isAssignableFrom(paramType)) {
return resolveNativeRequest(webRequest, paramType);
}

// HttpServletRequest required for all further argument types
return resolveArgument(paramType, resolveNativeRequest(webRequest, HttpServletRequest.class));
}

我们以HttpServletRequest为例,执行了resolveNativeRequest方法,拿到原生ServletRequest并返回

1
2
3
4
5
6
7
8
private <T> T resolveNativeRequest(NativeWebRequest webRequest, Class<T> requiredType) {
// 拿到原生ServletRequest,最后返回
T nativeRequest = webRequest.getNativeRequest(requiredType);
if (nativeRequest == null) {
throw new IllegalStateException("...");
}
return nativeRequest;
}

(举例三)Model, Map原理

ModelMethodProcessorMapMethodProcessor参数解析器实现了supportsParameter方法

1
2
3
4
5
6
// 参数为Model
@Override
public boolean supportsParameter(MethodParameter parameter) {
// 判断参数类型为Model
return Model.class.isAssignableFrom(parameter.getParameterType());
}
1
2
3
4
5
6
7
// 参数为Map
@Override
public boolean supportsParameter(MethodParameter parameter) {
// 判断参数类型为Map,且参数没有标注解
return (Map.class.isAssignableFrom(parameter.getParameterType()) &&
parameter.getParameterAnnotations().length == 0);
}

其次,它们都实现了resolveArgument方法

1
2
3
4
5
6
7
8
// 参数为Model
@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
// 从modelAndViewContainer中获得Model
return mavContainer.getModel();
}
1
2
3
4
5
6
7
8
// 参数为Map
@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
// 从modelAndViewContainer中获得Model
return mavContainer.getModel();
}

可以看到,无论是Model还是Map,返回的都是mavContainer里的ModelBindingAwareModelMap,根本上继承自MapModel),他们获取的都是同一个对象

  • 标题: SpringBoot源码系列(5):参数解析
  • 作者: 布鸽不鸽
  • 创建于 : 2023-05-16 11:06:36
  • 更新于 : 2023-06-26 09:30:39
  • 链接: https://xuedongyun.cn//post/13517/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论