源码分析SpringMVC解析请求参数原理
一、前言
在使用SpringMVC的时候,我们经常编写controller层的接口,并使用一些注解传递一些参数,那么这些参数是怎么封装进我们的请求的呢?SpringMVC是怎么解析这些参数的呢?本文主要通过源码分析SpringMVC解析请求参数的全过程。
二、准备工作
编写一个Controller类,里面写上一个接口方法
@ResponseBody
@RequestMapping("/get/{id}")
public List<Object> testHello(@PathVariable("id") String id,
@RequestParam String username,
Map<String, Object> map,
HttpServletRequest request) {
List<Object> list = new ArrayList<>();
list.add(id);
list.add(username);
return list;
}
在浏览器中输入URL保证接口可以正常返回:
http://localhost:8089/tracy/get/1?username=tracy
返回结果:
接口正常返回,准备工作结束。
三、源码调试
找到DispatcherServlet类,找到doDispatcher方法,以debug模式启动程序并访问接口,将断点定位至下图中的位置:
通过注释我们可以看出该方法是实际调用应用程序的方法,我们可以跟进去进行查看:
程序来到了RequestMappingHandlerAdapter类中的handleInternal方法,
其中handlerMethod就是我们访问的接口的对应的方法,SpringMVC利用反射帮我们进行调用。
在同样的方法中将断点运行至该处,此方法名为invokeHandlerMethod,可知这个方法便是SpringMVC对我们接口方法进行实际调用。
继续按照执行链进行查看,程序进入了invokeHandlerMethod内部,在这个方法中SpringMVC将我们的接口中的方法进行了一系列的前置操作,将接口方法封装为可调用的处理程序方法,并进行接下来的逻辑。
将程序运行至下图所示位置,可看到SpringMVC使用封装后的接口方法invocableMethod调用了invokeAndHandle方法,在这个方法中我们的接口被实际调用。
来到invokeAndHandle方法内部,可以看到方法内部直接调用了invokeForRequest方法,该方法将我们的请求进行了全部的处理,包括解析参数,处理逻辑,结果返回等步骤。
进入到invokeForRequest方法,方法内优先执行了getMethodArgumentsValues方法,根据名字我们可以推测在该方法中SpringMVC进行了方法参数的校验,并获取最终request中携带的参数。
来到getMethodArgumentValues方法体,里面将我们request中的所有的参数进行了解析,并最终封装至标注了@RequestMapping注解的方法的参数中。
这里根据我们的参数解析器判断该参数是否是我们解析器所支持的参数。
跟进方法我们可以发现SpringMVC优先从已缓存的解析器中寻找合适的解析器进行解析,若未找到,则进行循环遍历。本例中我们的id被@PathVariable修饰,可以被SpringMVC解析,而且缓存中已经有相应的解析器,则将结果直接进行返回。
重要
接下来是SpringMVC最核心的解析参数环节,在这里通过解析器调用resolveArgument方法对参数进行解析,并最终返回至参数数组。
点进resolveArgument方法进行查看,在这里SpringMVC对我们传递的参数进行真正的解析,包括参数名,参数值,并进行返回。
查看SpringMVC的返回结果:发现解析出id的值为1,和我们在url中输入的值一致。至此本次请求的第一个参数便解析完毕,其他被不同注解标注的参数也会被不同的适配器进行解析。
四、总结
SpringMVC使用建议:
1、创建Controller时尽量以单例模式创建,这样可以减少创建对象和销毁对象时的开销。并且尽量不要在Controller中声明类变量或实力变量,所有的参数均使用形参进行传递,可以有效避免线程安全的问题。
2、SpringMVC处理请求流程:
- DispatcherServlet中的doDispatcher方法进行响应,并执行至HandlerAdapter调用handle方法进行适配。
- 来到AbstractHandlerMethodAdapter类调用handleInternal方法,该方法最终被RequestMappingHandlerAdapter所实现,即适配所有被@RequestMapping修饰的Controller的方法。
- 在RequestMappingHandlerAdapter类中调用invokeHandlerMethod方法,此过程将Controller类中的方法转化为HttpMethod进行解析。
- 解析后的Method调用invokeAndHandle进行接口的调用访问。
- 在InvocableHandlerMethod类中的getMethodArgumentValues方法来解析客户端请求传递的数据。在解析之前为每个参数找到合适的参数解析器进行解析,并读取出最终的值。
- 将读取的值进行返回,返回给Controller端后进行后续的业务逻辑。