前言
在SpringBoot/SpringMVC中,我们能在Controller中解析出请求的参数,本文谈一谈其背后的原理。本文中SpringBoot版本号为2.7.5。
原文地址:https://xuedongyun.cn/post/13517/
参数解析原理
DispatcherServlet
之前的文章已经提到,DispatcherServlet
的核心方法是doDispatch
。在doDispatch
方法中,我们先通过HandlerMappings
找到了handler
。再为当前Handler
找到HandlerAdapter
。
这一部分可以查看我之前的博文复习:SpringBoot源码系列(4):请求映射。
mappedHandler = getHandler(processedRequest);
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
getHandlerAdapter
方法,遍历this.handlerAdapters
,来找到合适的HandlerAdapter
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
包含了四个,完成不同的功能
this.handlerAdapters = {ArrayList} size=4
0 = (RequestMappingHandlerAdapter) // 支持标注@RequestMapping
1 = (HandlerFunctionAdapter) // 支持函数式编程
2 = {HttpRequestHandlerAdapter}
3 = {SimpleControllerHandlerAdapter}
RequestMappingHandlerAdapter
通过步入可以看到,RequestMappingHandlerAdapter
(其父类AbstractHandlerMethodAdapter
)实现了supports
方法。
这里主要进行了类型判断,看handler
是否是HandlerMethod
类型。(RequestMappingHandlerAdapter
的supportsInternal
总返回true)
@Override
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
DispatcherServlet
现在回到DispatcherServlet中,开始使用handlerAdapter
真正执行handler
方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
RequestMappingHandlerAdapter
通过步入可以看到,RequestMappingHandlerAdapter
(其父类AbstractHandlerMethodAdapter
)实现了handle
方法。
@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
// 代码有删改
@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
方法。其核心在于请求值解析器和返回值处理器
// 代码有删改
@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);
}
}
// 请求值解析器
argumentResolvers = {ArrayList} size = 27
0 = {RequestParamMethodArgumentResolver}
1 = {RequestParamMapMethodArgumentResolver}
2 = {PathVariableMethodArgumentResolver}
3 = {PathVariableMapMethodArgumentResolver}
4 = {MatrixVariableMethodArgumentResolver}
5 = {MatrixVariableMapMethodArgumentResolver}
6 = {ServletModelAttributeMethodProcessor
7 = {RequestResponseBodyMethodProcessor}
...
// 返回值处理器
returnValueHandlers = {ArrayList} size = 15
0 = {ModelAndViewMethodReturnValueHandler}
1 = {ModelMethodProcessor)
2 = {ViewMethodReturnValueHandler}
3 = {ResponseBodyEmitterReturnValueHandler}
4 = {StreamingResponseBodyReturnValueHandler}
...
HandlerMethodArgumentResolver
我们可以简单看一下请求值解析器的接口设计。其中supportsParameter
方法用于判断解析器是否支持解析当前参数。若支持,则将调用resolveArgument
方法进行解析
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
。我们现在查看ServletInvocableHandlerMethod
的invokeAndHandle
方法
// 代码有删改
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
}
继续查看invokeForRequest
方法。可以看到,使用了getMethodArgumentValues
方法获取了所有请求参数的值。最终调用doInvoke
方法,利用反射执行了我们Controller
中的方法。
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 获取所有参数的值
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
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);
}
}
// 返回目标函数参数的值
return args;
}
HandlerMethodArgumentResolverComposite
我们之前调用了this.resolvers.supportsParameter(parameter)
用于判断参数解析器是否支持当前参数。现在来看一下其中的源码
@Override
public boolean supportsParameter(MethodParameter parameter) {
// 拿到参数解析器
return getArgumentResolver(parameter) != null;
}
@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
注解
if (parameter.hasParameterAnnotation(RequestParam.class)) {}
比如(第二个)RequestParamMapMethodArgumentResolver
解析器:参数要标@RequestParam
注解,参数要为Map
类型,@RequestParam
注解中要没有name
值
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)
用于解析参数,我们看一看其中源码
@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
值
@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
方法
@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
方法
@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
方法。参数需要为特定的类型
@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
方法。根据具体类型执行不同的操作。
@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
并返回
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原理
ModelMethodProcessor
,MapMethodProcessor
参数解析器实现了supportsParameter
方法
// 参数为Model
@Override
public boolean supportsParameter(MethodParameter parameter) {
// 判断参数类型为Model
return Model.class.isAssignableFrom(parameter.getParameterType());
}
// 参数为Map
@Override
public boolean supportsParameter(MethodParameter parameter) {
// 判断参数类型为Map,且参数没有标注解
return (Map.class.isAssignableFrom(parameter.getParameterType()) &&
parameter.getParameterAnnotations().length == 0);
}
其次,它们都实现了resolveArgument
方法
// 参数为Model
@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
// 从modelAndViewContainer中获得Model
return mavContainer.getModel();
}
// 参数为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
里的Model
(BindingAwareModelMap
,根本上继承自Map
和Model
),他们获取的都是同一个对象