请求参数解析
客户端请求在handlerMapping中找到对应handler后,将会继续执行
DispatchServlet
的
doPatch()
方法。
首先是找到handler对应的适配器。
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
进入到
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\");}
这里存在多个适配器,如图:
其中使用
@RequestMaping
注解修饰的控制器都将适配第一个适配器;而函数式方法将会使用第二个适配器。
跟踪请求,这里将会获得第一个适配器,判断也简单,如下:
public final boolean supports(Object handler) {return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));}
如果是
HandlerMethod
类型的处理器就采用这个适配器,而客户端请求正好对应的是
HandlerMethod
处理器。
找到适配器后,将会真正执行处理器逻辑。如下:
// Actually invoke the handler.mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
进入
RequestMappingHandlerAdapter
,执行适配器核心方法:
@Overrideprotected ModelAndView handleInternal(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ModelAndView mav;checkRequest(request);// Execute invokeHandlerMethod in synchronized block if required.if (this.synchronizeOnSession) {HttpSession session = request.getSession(false);if (session != null) {Object mutex = WebUtils.getSessionMutex(session);synchronized (mutex) {mav = invokeHandlerMethod(request, response, handlerMethod);}}else {// No HttpSession available -> no mutex necessarymav = invokeHandlerMethod(request, response, handlerMethod);}}else {// No synchronization on session demanded at all...mav = invokeHandlerMethod(request, response, handlerMethod);}if (!response.containsHeader(HEADER_CACHE_CONTROL)) {if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);}else {prepareResponse(response);}}return mav;}
其核心代码为实际执行处理器方法:
mav = invokeHandlerMethod(request, response, handlerMethod);
同样,我们打开
RequestMappingHandlerAdapter
中的
invokeHandlerMethod
方法:
@Nullableprotected ModelAndView invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod56chandlerMethod) 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);}//处理器对象装配完成,执行控制器方法invocableMethod.invokeAndHandle(webRequest, mavContainer);if (asyncManager.isConcurrentHandlingStarted()) {return null;}return getModelAndView(mavContainer, modelFactory, webRequest);}finally {webRequest.requestCompleted();}}
在这个关键方法中,首先执行请求对应的控制器逻辑,之后进行系列处理,根据返回值处理器处理返回值。
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {//执行控制器方法Object returnValue = inv56cokeForRequest(webRequest, mavContainer, providedArgs);setResponseStatus(webRequest);if (returnValue == null) {if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {disableContentCachingIfNecessary(webRequest);mavContainer.setRequestHandled(true);return;}}else if (StringUtils.hasText(getResponseStatusReason())) {mavContainer.setRequestHandled(true);return;}mavContainer.setRequestHandled(false);Assert.state(this.returnValueHandlers != null, \"No return value handlers\");try {//处理返回值this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);}catch (Exception ex) {if (logger.isTraceEnabled()) {logger.trace(formatErrorForReturnValue(returnValue), ex);}throw ex;}}
以下给出部分参数解析器及返回值处理器截图:
参数解析器。对应每一个参数(路径变量、矩阵变量、获得请求头、请求域等)的获取方式
返回值处理器。ModelAndView、ResponseBody等。每个处理器处理不同类别的返回值类型。
接下来,真正进入到最终执行method方法
invocableMethod.invokeAndHandle(webRequest, mavContainer);
,这里是真是执行控制器中映射的方法。
以下为获得参数列表对应值的逻辑,参数获取完成后将会执行真正的控制器逻辑。
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);}
进入解析逻辑,解析是对比每一个参数绑定的注解,如果注解一致将会使用对应的解析器将请求传递的参数值获取到。
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;}
查看解析器是否指出当前参数部分代码,可以了解到SpringMVC的缓存策略。
private15b0HandlerMethodArgumentResolver 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;}
参数列表
以下为获得的参数列表第一个参数部分属性
其对应的是控制器中
id
参数:
@GetMapping(value = \"/student01/{id}/car/{name}\")public Map<String, Object> testAnnotation(@PathVariable(name = \"id\") String id){Map<String, Object> map = new HashMap<>();map.put(\"id\", id);return map;}
以上即为获得请求Handler对应的适配器,处理参数映射、执行控制器逻辑、返回值处理的核心源码处理。