源码版本
spring-web:5.3.7
测试demo
@RestController
@RequestMapping("test/v1")
public class ZouController {
@GetMapping("/test")
public Student test(int a, int b) {
System.out.println("请求进来了,开始休眠");
System.out.println("调用成功");
Student test = new Student(null, 18, LocalDateTime.now());
test.setLocalDate(LocalDate.now());
return test;
}
@PostMapping("/post/test")
public String postTest() {
return "Success";
}
}
这里简单准备了一个Controller
然后使用postman
调用接口localhost:8080/test/v1/test?a=1&b=2
主要目标
了解整个执行过程核心原理
源码分析
我们都知道Spring MVC的核心路口肯定是从DispatcherServlet
的doDispatch
方法开始的,具体原因可以Google。这里不展开讲解,因为这不是本文重点
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 = HttpMethod.GET.matches(method);
if (isGet || HttpMethod.HEAD.matches(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 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);
}
}
}
}
这段方法很长,我们一步一步分析。首先看这个方法
mappedHandler = getHandler(processedRequest);
这里主要是获取一个HandlerExecutionChain
这个类里面有什么属性呢?我们debug看一下

可以看到HandlerExecutionChain
对象就已经通过url获取到了对应的controller和method。所以我们想要了解Spring MVC 是如何通过url找到对应的controller和method,就必须看看是如何获取到HandlerExecutionChain
对象的
获取HandlerExecutionChain对象
我们进入getHandler
方法
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
可以看到这里的HandlerExecutionChain
是通过List<HandlerMapping> handlerMappings
遍历调用getHandler
获取的,我们debug看看有多少个HandlerMapping
-
RequestMappingHandlerMapping -
BeanNameUrlHandlerMapping -
RouterFunctionMapping -
SimpleUrlHandlerMapping -
WelcomePageHandlerMapping:
都是处理请求的处理器,比如WelcomePageHandlerMapping
就是专门用来处理默认请求/
的,例如:http://localhost:8080/
,就会返回规定路径下的index.html
。其他处理器就不一一介绍了,感兴趣可以自己去研究,今天我们要研究的重点也是最常用的RequestMappingHandlerMapping
处理器。
RequestMappingHandlerMapping
会将 @Controller, @RequestMapping 注解的类方法注册为 handler,每个请求url可以对应一个 handler method 来处理
RequestMappingHandlerMapping
我们看看RequestMappingHandlerMapping
的类UML图
我们会发现
RequestMappingHandlerMapping
并没有自己实现HandlerMapping
接口,是通过继承抽象类AbstractHandlerMapping
间接实现了HandlerMapping
,而方法getHandler
也是直接调用AbstractHandlerMapping
中的getHandler
方法,所以我们直接去分析AbstractHandlerMapping
的getHandler
方法即可
AbstractHandlerMapping
记住我们的主线目标如何获取到HandlerExecutionChain
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
// Ensure presence of cached lookupPath for interceptors and others
if (!ServletRequestPathUtils.hasCachedPath(request)) {
initLookupPath(request);
}
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !DispatcherType.ASYNC.equals(request.getDispatcherType())) {
logger.debug("Mapped to " + executionChain.getHandler());
}
if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
CorsConfiguration config = getCorsConfiguration(handler, request);
if (getCorsConfigurationSource() != null) {
CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
config = (globalConfig != null ? globalConfig.combine(config) : config);
}
if (config != null) {
config.validateAllowCredentials();
}
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
可以看到有个方法
Object handler = getHandlerInternal(request);
可以看到getHandlerInternal
方法就映射到了具体的controller
的method
,所以我们在深入看看getHandlerInternal
方法
getHandlerInternal
方法是个抽象方法,调用子类RequestMappingInfoHandlerMapping
的getHandlerInternal
方法
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
request.removeAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
try {
return super.getHandlerInternal(request);
}
finally {
ProducesRequestCondition.clearMediaTypesAttribute(request);
}
}
但是子类又调回了父类AbstractHandlerMethodMapping
的getHandlerInternal
方法,所以实际获取映射的方法还是父类的getHandlerInternal
方法,我们看看父类的getHandlerInternal
方法
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = initLookupPath(request);
this.mappingRegistry.acquireReadLock();
try {
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
可以看到这里这个实际其实就是用到了模板设计模式
通过debug我们可以很清晰的看到
initLookupPath
方法通过request获取到了我们请求的url:/test/v1/test
,而实际通过url获取到HandlerMethod
还得去看lookupHandlerMethod
方法
AbstractHandlerMethodMapping lookupHandlerMethod方法
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
}
if (!matches.isEmpty()) {
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);
bestMatch = matches.get(0);
if (logger.isTraceEnabled()) {
logger.trace(matches.size() + " matching mappings: " + matches);
}
if (CorsUtils.isPreFlightRequest(request)) {
for (Match match : matches) {
if (match.hasCorsConfig()) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
}
}
else {
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.getHandlerMethod().getMethod();
Method m2 = secondBestMatch.getHandlerMethod().getMethod();
String uri = request.getRequestURI();
throw new IllegalStateException(
"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
}
}
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.getHandlerMethod();
}
else {
return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
}
}
这里代码有点长,我们挑重点看,首先是this.mappingRegistry
这个对象,我们看看里面有什么

可以看到这里面有很多个RequestMappingInfo
,看属性是不是很熟悉,就是我们写的controller和method,包括注解@RequestMapping
上的url也在 所以如何通过url获取到对应的controller和method在这里就得到了答案,至于this.mappingRegistry
中的RequestMappingInfo
对象是如何初始化填充的,不在我们之类的重点,但是我们也可以很容易猜到无非就是在Spring容器启动的时候去扫描类上有@controller注解的类,然后解析方法转换为RequestMappingInfo
对象
方法执行
回到我们的主线类DispatcherServlet
的doDispatch
方法中。我们会发现在通过url获取到类和方法后,我们执行方法是在这一行执行的
// 这里获取到的 HandlerAdapter 实际为 RequestMappingHandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
getHandlerAdapter 中默认有四个HandlerAdapter
其中这里获取到的是RequestMappingHandlerAdapter
最后执行方法的逻辑也就是在RequestMappingHandlerAdapter
的handleInternal
方法
protected ModelAndView handleInternal(HttpServletRequest request,
// 省略部分代码
else {
// No synchronization on session demanded at all...
mav = invokeHandlerMethod(request, response, handlerMethod);
}
其中稍微在核心一点的一个方法就是方法参数的绑定 这段代码的逻辑是在InvocableHandlerMethod
中的getMethodArgumentValues
方法

关于方法参数的绑定这里提一点,如果使用注解
@RequestParam
是直接可以获取到方法参数名字的,但是如果没有使用注解@RequestParam
在jdk 1.8之前都只能通过字节码技术去获取方法参数名字。
整个MVC的核心流程到这里就结束了。
总结
其实整体流程看到是很长的,核心思路还是很清晰的。就是通过请求url找到对应的controller的method。然后执行对应的方法,然后返回数据。我们这次只是大致过了一遍整体流程,实际比较多的细节我们并没有重点分析,后续有机会在继续研究
原文始发于微信公众号(小奏技术):从源码角度Debug一步一步分析Spring MVC一次接口调用的过程
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/30016.html