SpringMVC:请求参数解析原理

测试

@Controller
public class HelloController {
    @RequestMapping("/testVar/{id}")
    @ResponseBody
    public String testVar(@PathVariable Integer id){
        return "ok"+ id;
    }
}

image-20210406202135783

原理剖析

看SpringMVC源码还得从DispatcherServlet的doDispatch方法开始看

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

		try {
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

                //获取当前请求的处理器
				// Determine handler for the current request.
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

                //找到处理器适配器
				// Determine handler adapter for the current request.
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

                //执行目标方法,并返回ModelAndView
				// Actually invoke the handler.
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}

				applyDefaultViewName(processedRequest, mv);
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			catch (Throwable err) {
				// As of 4.3, we're processing Errors thrown from handler methods as well,
				// making them available for @ExceptionHandler methods and other scenarios.
				dispatchException = new NestedServletException("Handler dispatch failed", err);
			}
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Throwable err) {
			triggerAfterCompletion(processedRequest, response, mappedHandler,
					new NestedServletException("Handler processing failed", err));
		}
		finally {
			if (asyncManager.isConcurrentHandlingStarted()) {
				// Instead of postHandle and afterCompletion
				if (mappedHandler != null) {
					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			}
			else {
				// Clean up any resources used by a multipart request.
				if (multipartRequestParsed) {
					cleanupMultipart(processedRequest);
				}
			}
		}
	}

所以我们只需要分析HandlerAdapter的handle方法,该方法调用了handleInternal方法

image-20210406203201568

image-20210406203341846

进入invokeHandlerMethod方法:

image-20210406203728399

而这些参数解析器都实现了HandlerMethodArgumentResolver接口

public interface HandlerMethodArgumentResolver {

	//是否支持解析该参数
	boolean supportsParameter(MethodParameter parameter);

	//调用解析方法,进行解析
	@Nullable
	Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;

}

继续断点,下面又设置了返回值处理器ReturnValueHandlers(不在本文讨论之内),总共15种

image-20210406204209955

断点往下走,到invocableMethod.invokeAndHandle(执行并处理)

image-20210406204314647

image-20210406204453407

image-20210406204700961

调用doInvoke,在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);
            //这里的providedArgs为空,而findProvidedArgument方法里面直接判断providedArgs是否为空,
            //如果为空,直接返回null,所以这里不是请求参数解析的地方
			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,所以这里就是解析请求参数的地方
				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;
	}

源代码如下:getArgumentResolver会依次遍历所有的参数解析器去匹配

image-20210406205916958

因为我们标注了@PathVariable注解,那根据名字,我猜测可能用的是PathVariableMethodArgumentResolver。源码说话:

image-20210406210745553

现在找到了resolver,那我们看它resolveArgument是怎么执行的

image-20210406211448909

resolveName方法:

image-20210406212014528

至此,Spring MVC的请求参数解析结束。

扩展

通过上述源码分析,我们知道spring mvc请求参数解析的原理。

而如何解析,都在HandlerMethodArgumentResolver接口的实现类中。所以我们能在参数位置写那些类型的参数,就需要根据HandlerMethodArgumentResolver了

原文地址:https://www.cnblogs.com/wwjj4811/p/14623854.html