测试
@Controller
public class HelloController {
@RequestMapping("/testVar/{id}")
@ResponseBody
public String testVar(@PathVariable Integer id){
return "ok"+ id;
}
}
原理剖析
看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方法
进入invokeHandlerMethod方法:
而这些参数解析器都实现了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种
断点往下走,到invocableMethod.invokeAndHandle(执行并处理)
调用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会依次遍历所有的参数解析器去匹配
因为我们标注了@PathVariable注解,那根据名字,我猜测可能用的是PathVariableMethodArgumentResolver。源码说话:
现在找到了resolver,那我们看它resolveArgument是怎么执行的
resolveName方法:
至此,Spring MVC的请求参数解析结束。
扩展
通过上述源码分析,我们知道spring mvc请求参数解析的原理。
而如何解析,都在HandlerMethodArgumentResolver接口的实现类中。所以我们能在参数位置写那些类型的参数,就需要根据HandlerMethodArgumentResolver了