上一篇文章讲到了Servlet与DispatcherServlet, Servlet回答了Tomcat 在初始化时做什么, 请求进来做什么,下面也分别以初始化和处理请求两个方面对DispatcherServlet进行分析。分析Spring源码的一个比较好的方式,是写一个简单的Demo,然后使用IDE的Debug模式,断点调试一下,看看具体是如何执行的。
这里以spring-boot的一个demo为例,spring-webmvc版本为5.2.4。为了便于分析, 先看请求进来时有哪些主要的类参与了工作,然后再看这些类是如何初始化的。
1. 请求进来时做什么

DispatcherServlet处理请求主要流程如上图所示,下面进行详细分析:DispatcherServlet内部通过doService处理请求, 而doService内调用doDispatch(),所以先把目光移到doDispatch方法上,doDispatch的源码如下所示:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine handler for the current request.
HandlerExecutionChain mappedHandler = getHandler(processedRequest);
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
去掉不相关的逻辑,主要有三个步骤,并引出今天的两个主角HandlerMapping与HandlerAdapter
1.1 getHandler
getHandler方法根据请求,得到HandlerExecutionChain,HandlerExecutionChain内部的handler为这次请求的处理方法,主要逻辑如下:
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;
}
this.handlerMappings是一个List,值为RequestMappingHandlerMapping、 WelcomePageHandlerMapping、BeanNamUrlHandlerMapping、RouterFunctionMapping、SimpleUrlHandlerMapping。 其中最常用的是RequestMappingHandlerMapping , 通过在Controller上加@RequestMapping注解,向Spring MVC注册,例如
@RequestMapping(value = {"/{id}/get", "/get2"}, method = RequestMethod.GET)
public String test(@PathVariable Optional<Integer> id,
@RequestParam String parameter1,
@RequestParam(required = false) Color color) {
String stringId = id.isPresent() ? id.get().toString() : "";
String stringColor = color != null ? color.toString() : "";
return stringId + parameter1 + stringColor;
}
RequestMappingHandlerMapping 中主要的是mappingRegistry的registry,是一个Map结构,其中一个entry正好与上面的代码对应:
- key是RequestMappingInfo信息
例如patternsCondition: [/{id}/get || /get2], methodsCondition: Get - value中主要的是HandlerMethod
例如这里是com.liuil.springboot.controller.DemoController#test(Optional, String, Color)

最终得到的mappedHandler如下所示, 是一个HandlerExecutionChain,内部有:
- handler: 处理请求的方法
- interceptors: 拦截器

1.2 getHandlerAdapter
getHandlerAdapter获取HandlerAdapter,内部包含一个handle方法,负责调用真实处理请求的方法并返回一个ModelAndView。HandlerAdapter里面有一些常见的处理,比如argumentResolvers可以用来处理请求的参数,messageConverts是作消息转换等等。主要代码如下:
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
...
}
handlerAdapters 是一个list,值为: RequestMappingHandlerAdapter、HandlerFunctionAdapter、HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter。 这里返回的是RequestMappingHandlerAdapter, 值为:

1.3 ha.handle
调用方法,处理请求,主要调用的方法是InvocableHandlerMethod#invokeForRequest, 主要的逻辑:
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
...
return doInvoke(args);
}
- 首先通过getMethodArgumentValues从request中解析出参数,参数的解析是通过Handler中的argumentResolvers来实现的:
- PathVariableMethodArgumentResolver 负责解析url path中的参数
- RequestParamMethodArgumentResolver负责解析url中query string的参数
对于一些复杂的转换,还可以实现org.springframework.core.convert.converter.Converter,定义自己的converter - RequestResponseBodyMethodProcessor 负责解析request body中的参数,内部会调用messageConverter, 一般是对application/json格式的json字符串反序列化得到。默认的是MappingJackson2HttpMessageConverter, 可以配置成FastJsonMessageConverter。
解析得到参数列表, 这是的请求是 /get2?parameter1=1&color=red

- 解析得到参数后,通过doInvoke(args) 调用对应的函数(通过反射??),并返回执行结果
2. 初始化时做什么
DispatcherServlet的初始化流程如下图所示,Spring Boot中的DispatcherServlet是通过 DispatcherServletAutoConfiguration配置的完成的。
通过 load-on-startup控制servlet容器什么时候加载这个servlet
- load-on-startup>=0
Servlet容器(Tomcat)在启动的时候就加载这个servlet(实例化并调用其init()方法)。
例如: 在application.properties 中添加spring.mvc.servlet.load-on-startup=1 - load-on-startup 未指定或<0 ,
servlet在请求尝试访问他的时候才启动。

initStrategies 初始化Spring的九大组件
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
下面重点介绍HandlerMapping和HandlerAdapter的初始化
2.1 HandlerMapping初始化
initHandlerMappings初始化HandlerMapping
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
...
}
BeanFactoryUtils.beansOfTypeIncludingAncestors用于从应用上下文中根据给定的类型或者子类型返回所有bean, 如果当前的bean工厂是HierarchicalBeanFactory,也会递归获取在父bean工厂中定义的bean。
这是里找所有HandlerMapping类型的Bean,即为之前的RequestMappingHandlerMapping, WelcomePageHandlerMapping, BeanNamUrlHandlerMapping,RouterFunctionMapping,SimpleUrlHandlerMapping。此时RequestMapping的信息已经注册到RequestMappingHandlerMapping的mappingRegistry中。如何做到的呢,答案在RequestMappingHandlerMapping中。

RequestMappingHandlerMapping 实现了InitializingBean,覆写了afterPropertiesSet 方法, 调用如下:

真正初始化的方法在AbstractHandlerMethodMapping#detectHandlerMethods中:
- 获得所有容器托管的beanNames, 遍历beanNames 判断对应的类是否有RequestMapping 或Controller注解 ,这里的handlerType是DemoController.class
- 通过getMappingForMethod方法,找到所有带有RequestMapping注解的方法,创建Method和RequestMappingInfo的映射
- 调用registerHandlerMethod ,将handler, invocableMethod, mapping注册到mappingRegistry中。
注意invocableMethod是通过AopUtils.selectInvocableMethod得到方法本身,或者方法的代理(AoP增强用)。

protected void detectHandlerMethods(Object handler) {
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
Class<?> userType = ClassUtils.getUserClass(handlerType);
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
});
if (logger.isTraceEnabled()) {
logger.trace(formatMappings(userType, methods));
}
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
有关HandlerMapping初始化,更详细的请参考:
Spring Mvc 源码解析(三)— HandlerMapping初始化
2.2 HandlerAdapter初始化
HandlerAdapter的初始化与HandlerMapping类似, 也是先拿到所有HandlerAdapter类型的bean,最主要的RequestMappingHandlerAdapter 也是在afterPropertiesSet方法中完成对argumentResolvers、initBinderArgumentResolvers、returnValueHandlers的初始化。具体可参考: SpringMVC之源码分析--HandlerAdapter(二)
public void afterPropertiesSet() {
// Do this first, it may add ResponseBody advice beans
initControllerAdviceCache();
if (this.argumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.initBinderArgumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.returnValueHandlers == null) {
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}
总结
DispatcherServlet完整的初始化和处理请求的时序图如下所示, 图片来源于:
Spring MVC中的HandlerMapping与HandlerAdapter的关系

网友评论