前言 在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
类型。(RequestMappingHandlerAdapter
的supportsInternal
总返回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); 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); if (this .argumentResolvers != null ) { invocableMethod.setHandlerMethodArgumentResolvers(this .argumentResolvers); } 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
。我们现在查看ServletInvocableHandlerMethod
的invokeAndHandle
方法
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(); 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); 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(); if (WebRequest.class.isAssignableFrom(paramType)) { if (!paramType.isInstance(webRequest)) { throw new IllegalStateException ("..." ); } return webRequest; } if (ServletRequest.class.isAssignableFrom(paramType) || MultipartRequest.class.isAssignableFrom(paramType)) { return resolveNativeRequest(webRequest, paramType); } 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) { T nativeRequest = webRequest.getNativeRequest(requiredType); if (nativeRequest == null ) { throw new IllegalStateException ("..." ); } return nativeRequest; }
(举例三)Model, Map原理 ModelMethodProcessor
,MapMethodProcessor
参数解析器实现了supportsParameter
方法
1 2 3 4 5 6 @Override public boolean supportsParameter (MethodParameter parameter) { return Model.class.isAssignableFrom(parameter.getParameterType()); }
1 2 3 4 5 6 7 @Override public boolean supportsParameter (MethodParameter parameter) { return (Map.class.isAssignableFrom(parameter.getParameterType()) && parameter.getParameterAnnotations().length == 0 ); }
其次,它们都实现了resolveArgument
方法
1 2 3 4 5 6 7 8 @Override @Nullable public Object resolveArgument (MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { return mavContainer.getModel(); }
1 2 3 4 5 6 7 8 @Override @Nullable public Object resolveArgument (MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { return mavContainer.getModel(); }
可以看到,无论是Model
还是Map
,返回的都是mavContainer
里的Model
(BindingAwareModelMap
,根本上继承自Map
和Model
),他们获取的都是同一个对象