基于:SpringMVC 启动与请求处理流程解析
什么是 DispatcherServlet?
SpringMVC 基于 Servlet,DispatcherServlet 是 SpringMVC 的核心组件,本身是一个 Servlet,负责请求的分发,其继承关系如下:
GenericServlet (javax.servlet)↑
HttpServlet (javax.servlet.http)↑
HttpServletBean (org.springframework.web.servlet)↑
FrameworkServlet (org.springframework.web.servlet)↑
DispatcherServlet (org.springframework.web.servlet)
其中 GenericServlet 实现了 Servlet 接口。
我们在使用 SpringMVC 时,传统的方式是在 web.xml 中配置 DispatcherServlet,比如:
<web-app><servlet><servlet-name>app</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/spring.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>app</servlet-name><url-pattern>/app/*</url-pattern></servlet-mapping>
</web-app>
只要定义这样的一个 web.xml,然后启动 Tomcat,那么我们就能正常使用 SpringMVC 了。
在启动 Tomcat 的过程中:
- Tomcat 会先创建 DispatcherServlet 对象
- 然后调用 DispatcherServlet 对象的 init() 方法
init() 方法最终会创建一个 Spring 容器,并且添加一个 ContextRefreshListener 监听器,该监听器会监听 ContextRefreshedEvent 事件(Spring 容器启动完成后就会发布这个事件)。
Spring 容器启动完成后,就会执行 ContextRefreshListener 中的 onApplicationEvent() 方法,从而最终会执行 DispatcherServlet 中的 initStrategies() 方法,这个方法中会初始化更多内容:
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。
注册 ContextRefreshListener 的堆栈信息
![]() |
ContextRefreshListener 调用 initStrategies() 的堆栈信息
![]() |
什么是 Handler?
Handler 表示请求处理器,在 SpringMVC 中有四种 Handler:
- 实现了 Controller 接口的 Bean 对象
- 实现了 HttpRequestHandler 接口的 Bean 对象
- 添加了 @RequestMapping 注解的方法
- 一个 HandlerFunction 对象
比如实现了 Controller 接口的 Bean 对象:
@Component("/test")
public class TestBeanNameController implements Controller {@Overridepublic ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {System.out.println("test");return new ModelAndView();}
}
实现了 HttpRequestHandler 接口的 Bean 对象:
@Component("/test")
public class TestBeanNameController implements HttpRequestHandler {@Overridepublic void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("test");}
}
添加了 @RequestMapping 注解的方法:
@RequestMapping
@Component
public class TestController {@Autowiredprivate TestService testService;@RequestMapping(method = RequestMethod.GET, path = "/test")@ResponseBodypublic String test(String username) {return "test";}}
一个 HandlerFunction 对象(以下代码中有两个):
@ComponentScan("com.test")
@Configuration
public class AppConfig {@Beanpublic RouterFunction<ServerResponse> person() {return route().GET("/app/person", request -> ServerResponse.status(HttpStatus.OK).body("Hello GET")).POST("/app/person", request -> ServerResponse.status(HttpStatus.OK).body("Hello POST")).build();}}
什么是 HandlerMapping?
HandlerMapping 负责去寻找 Handler,并且保存路径和 Handler 之间的映射关系。
默认的 HandlerMapping 在前面提到的 initStrategies() 方法中创建。
默认 HandlerMapping 创建过程
![]() |
![]() |
![]() |
![]() |
![]() |
因为有不同类型的 Handler,所以在 SpringMVC 中会由不同的 HandlerMapping 来负责寻找 Handler,比如:
- BeanNameUrlHandlerMapping:负责 Controller 接口和 HttpRequestHandler 接口
- RequestMappingHandlerMapping:负责 @RequestMapping 的方法
- RouterFunctionMapping:负责 RouterFunction 以及其中的 HandlerFunction
HandlerMapping 作为 Bean 被创建,创建时 Spring 将会调用其 afterPropertiesSet 方法,该方法完成 Handler 的寻找过程。
BeanNameUrlHandlerMapping 寻找 Handler 的流程:
- 找出 Spring 容器中所有的 beanName
- 判断 beanName 是不是以
/开头 - 如果是,则把它当作一个 Handler,并把 beanName 作为 key,bean 对象作为 value 存入 handlerMap 中
- handlerMap 就是一个 Map 集合
RequestMappingHandlerMapping 的寻找流程:
- 找出 Spring 容器中所有 beanType
- 判断 beanType 是不是有 @Controller 注解,或者是不是有 @RequestMapping 注解
- 判断成功则继续找 beanType 中加了 @RequestMapping 的 Method
- 并解析 @RequestMapping 中的内容,比如 method、path,封装为一个 RequestMappingInfo 对象
- 最后把 RequestMappingInfo 对象做为 key,Method 对象封装为 HandlerMethod 对象后作为 value,存入 registry 中
- registry 就是一个 Map 集合
RequestMappingHandlerMapping 的 Handler 寻找流程调用堆栈
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
RouterFunctionMapping 的寻找流程会有些区别,但大体差不多,相当于是一个 path 对应一个 HandlerFunction。
各个 HandlerMapping 除开负责寻找 Handler 并记录映射关系之外,还需要根据请求路径找到对应的 Handler,在源码中这三个 HandlerMapping 有一个共同的父类 AbstractHandlerMapping:
![]() |
AbstractHandlerMapping 实现了 HandlerMapping 接口,并实现了 getHandler(HttpServletRequest request) 方法。
AbstractHandlerMapping 会负责调用子类的 getHandlerInternal(HttpServletRequest request) 方法从而找到请求对应的 Handler,然后 AbstractHandlerMapping 负责将 Handler 和应用中所配置的 HandlerInterceptor 整合成为一个 HandlerExecutionChain 对象。
所以寻找 Handler 的源码实现在各个 HandlerMapping 子类的 getHandlerInternal() 中,根据请求路径找到 Handler 的过程并不复杂,因为路径和 Handler 的映射关系已经存在 Map 中了。
那么当 DispatcherServlet 接收到一个请求时,该利用哪个 HandlerMapping 来寻找 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;
}
很简单,就是遍历,找到就返回,默认顺序为:
![]() |
BeanNameUrlHandlerMapping 的优先级最高。
什么是 HandlerAdapter?
找到了 Handler 之后,接下来就该去执行了,比如执行下面这个 test():
@RequestMapping(method = RequestMethod.GET, path = "/test")
@ResponseBody
public String test(String username) {return "test";
}
但是由于有不同种类的 Handler,所以执行方式是不一样的,再来总结一下 Handler 的类型:
- 实现了 Controller 接口的 Bean 对象,执行的是 Bean 对象中的 handleRequest()
- 实现了 HttpRequestHandler 接口的 Bean 对象,执行的是 Bean 对象中的 handleRequest()
- 添加了 @RequestMapping 注解的方法,具体为一个 HandlerMethod,执行的就是当前加了注解的方法
- 一个 HandlerFunction 对象,执行的是 HandlerFunction 对象中的 handle()
所以,按逻辑来说,找到 Handler 之后,我们得判断它的类型,比如代码可能是这样的:
Object handler = mappedHandler.getHandler();
if (handler instanceof Controller) {((Controller)handler).handleRequest(request, response);
} else if (handler instanceof HttpRequestHandler) {((HttpRequestHandler)handler).handleRequest(request, response);
} else if (handler instanceof HandlerMethod) {((HandlerMethod)handler).getMethod().invoke(...);
} else if (handler instanceof HandlerFunction) {((HandlerFunction)handler).handle(...);
}
但是 SpringMVC 并不是这么写的,而是采用的适配器模式,把不同种类的 Handler 适配成一个 HandlerAdapter,后续再执行 HandlerAdapter 的 handle() 方法就能执行不同种类 Handler 对应的方法。
默认的 HandlerAdapter 也在前面提到的 initStrategies() 方法中创建,创建过程和 HandlerMapping 创建过程类似。
针对不同的 Handler,会有不同的适配器:
- HttpRequestHandlerAdapter
- SimpleControllerHandlerAdapter
- RequestMappingHandlerAdapter
- HandlerFunctionAdapter
适配逻辑为:
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {if (this.handlerAdapters != null) {for (HandlerAdapter adapter : this.handlerAdapters) {if (adapter.supports(handler)) {return adapter;}}}throw new ServletException("No adapter for handler [" + handler +"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
传入 handler,遍历上面四个 Adapter,谁支持就返回谁,比如判断的代码依次为:
public boolean supports(Object handler) {return (handler instanceof HttpRequestHandler);
}public boolean supports(Object handler) {return (handler instanceof Controller);
}public final boolean supports(Object handler) {return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}public boolean supports(Object handler) {return handler instanceof HandlerFunction;
}
根据 Handler 找出了对应的 HandlerAdapter 后,就执行具体 HandlerAdapter 对象的 handle() 方法了,比如:
HttpRequestHandlerAdapter 的 handle():
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {((HttpRequestHandler) handler).handleRequest(request, response);return null;
}
SimpleControllerHandlerAdapter 的 handle():
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {return ((Controller) handler).handleRequest(request, response);
}
HandlerFunctionAdapter 的 handle():
HandlerFunction<?> handlerFunction = (HandlerFunction<?>) handler;
serverResponse = handlerFunction.handle(serverRequest);
因为这三个接收的直接就是 Request 对象,不用 SpringMVC 做额外的解析,所以比较简单,比较复杂的是 RequestMappingHandlerAdapter,它执行的是加了 @RequestMapping 的方法,而这种方法的写法可以是多种多样,SpringMVC 需要根据方法的定义去解析 Request 对象,从请求中获取出对应的数据然后传递给方法,并执行。
另:this.handlerAdapters 逻辑上组成了一个处理器链,for 循环遍历处理器链,逐个调用 supports() 方法判断是否支持,如果支持就执行 handle() 方法,该处理过程和责任链模式有些相像,只是责任链模式中通常是在处理器类内部持有另一个处理器,并在处理器内部调用 supports() 方法判断是否支持处理,而这里相当于将逐个调用 supports() 方法的过程放在了外部。
方法参数解析
当 SpringMVC 接收到请求,并找到了对应的 Method 之后,就要执行该方法了,不过在执行之前需要根据方法定义的参数信息,从请求中获取出对应的数据,然后将数据传给方法并执行。
一个 HttpServletRequest 通常有:
- request parameter
- request attribute
- request session
- request header
- request body
比如如下几个方法:
public String test(String username) {return "test";
}
表示要从 request parameter 中获取 key 为 username 的 value。
public String test(@RequestParam("uname") String username) {return "test";
}
表示要从 request parameter 中获取 key 为 uname 的 value。
public String test(@RequestAttribute String username) {return "test";
}
表示要从 request attribute 中获取 key 为 username 的 value。
public String test(@SessionAttribute String username) {return "test";
}
表示要从 request session 中获取 key 为 username 的 value。
public String test(@RequestHeader String username) {return "test";
}
表示要从 request header 中获取 key 为 username 的 value。
public String test(@RequestBody String username) {return "test";
}
表示获取整个请求体。
所以,我们发现 SpringMVC 要去解析方法参数,看该参数到底是要获取请求中的哪些信息。
而这个过程,源码中是通过 HandlerMethodArgumentResolver 来实现的,比如:
- RequestParamMethodArgumentResolver:负责处理 @RequestParam
- RequestHeaderMethodArgumentResolver:负责处理 @RequestHeader
- SessionAttributeMethodArgumentResolver:负责处理 @SessionAttribute
- RequestAttributeMethodArgumentResolver:负责处理 @RequestAttribute
- RequestResponseBodyMethodProcessor:负责处理 @RequestBody
- 还有很多其他的...
而在判断某个参数该由哪个 HandlerMethodArgumentResolver 处理时,也就是遍历所有的 HandlerMethodArgumentResolver,哪个能支持处理当前这个参数就由哪个处理:
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);if (result == null) {for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {if (resolver.supportsParameter(parameter)) {result = resolver;this.argumentResolverCache.put(parameter, result);break;}}}return result;
}
比如:
@RequestMapping(method = RequestMethod.GET, path = "/test")
@ResponseBody
public String test(@RequestParam @SessionAttribute String username) {System.out.println(username);return "test";
}
以上代码的 username 将对应 RequestParam 中的 username,而不是 session 中的,因为在源码中 RequestParamMethodArgumentResolver 更靠前。
当然 HandlerMethodArgumentResolver 也会负责从 request 中获取对应的数据,对应的是 resolveArgument() 方法。
比如 RequestParamMethodArgumentResolver:
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);if (servletRequest != null) {Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {return mpArg;}}Object arg = null;MultipartRequest multipartRequest = request.getNativeRequest(MultipartRequest.class);if (multipartRequest != null) {List<MultipartFile> files = multipartRequest.getFiles(name);if (!files.isEmpty()) {arg = (files.size() == 1 ? files.get(0) : files);}}if (arg == null) {String[] paramValues = request.getParameterValues(name);if (paramValues != null) {arg = (paramValues.length == 1 ? paramValues[0] : paramValues);}}return arg;
}
核心是:
if (arg == null) {String[] paramValues = request.getParameterValues(name);if (paramValues != null) {arg = (paramValues.length == 1 ? paramValues[0] : paramValues);}
}
按同样的思路,可以找到方法中每个参数所要求的值,从而执行方法,得到方法的返回值。
方法返回值解析
而方法返回值,也会分为不同的情况。比如有没有加 @ResponseBody 注解,如果方法返回一个 String:
- 加了 @ResponseBody 注解:表示直接将这个 String 返回给浏览器
- 没有加 @ResponseBody 注解:表示应该根据这个 String 找到对应的页面,把页面返回给浏览器
在 SpringMVC 中,会利用 HandlerMethodReturnValueHandler 来处理返回值:
- RequestResponseBodyMethodProcessor:处理加了 @ResponseBody 注解的情况
- ViewNameMethodReturnValueHandler:处理没有加 @ResponseBody 注解并且返回值类型为 String 的情况
- ModelMethodProcessor:处理返回值是 Model 类型的情况
- 还有很多其他的...
我们这里只讲 RequestResponseBodyMethodProcessor,因为它会处理加了 @ResponseBody 注解的情况,也是目前我们用得最多的情况。
RequestResponseBodyMethodProcessor 相当于会把方法返回的对象直接响应给浏览器,如果返回的是一个字符串,那么好说,直接把字符串响应给浏览器,那如果返回的是一个 Map 呢?是一个 User 对象呢?该怎么把这些复杂对象响应给浏览器呢?
这时 SpringMVC 会利用 HttpMessageConverter 来处理,SpringMVC 有提供一些默认的 HttpMessageConverter:
- ByteArrayHttpMessageConverter:处理返回值为字节数组的情况,把字节数组返回给浏览器
- StringHttpMessageConverter:处理返回值为字符串的情况,把字符串按指定的编码序列号后返回给浏览器
- AllEncompassingFormHttpMessageConverter:处理返回值为 MultiValueMap 对象的情况
- ...
// WebMvcConfigurationSupport
protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {messageConverters.add(new ByteArrayHttpMessageConverter());messageConverters.add(new StringHttpMessageConverter());messageConverters.add(new ResourceHttpMessageConverter());messageConverters.add(new ResourceRegionHttpMessageConverter());// ...messageConverters.add(new AllEncompassingFormHttpMessageConverter());// ...
}
StringHttpMessageConverter 的源码比较简单:
protected void writeInternal(String str, HttpOutputMessage outputMessage) throws IOException {HttpHeaders headers = outputMessage.getHeaders();if (this.writeAcceptCharset && headers.get(HttpHeaders.ACCEPT_CHARSET) == null) {headers.setAcceptCharset(getAcceptedCharsets());}Charset charset = getContentTypeCharset(headers.getContentType());StreamUtils.copy(str, charset, outputMessage.getBody());
}
先看有没有设置 Content-Type,如果没有设置则取默认的,默认为 ISO-8859-1,所以默认情况下返回中文会乱码,可以通过以下来中方式来解决:
@RequestMapping(method = RequestMethod.GET, path = "/test", produces = {"application/json;charset=UTF-8"})
@ResponseBody
public String test() {return "测试";
}
@ComponentScan("com.test")
@Configuration
@EnableWebMvc
public class AppConfig implements WebMvcConfigurer {@Overridepublic void configureMessageConverters(List<HttpMessageConverter<?>> converters) {StringHttpMessageConverter messageConverter = new StringHttpMessageConverter();messageConverter.setDefaultCharset(StandardCharsets.UTF_8);converters.add(messageConverter);}
}
不过以上四个 Converter 是不能处理 Map 对象或 User 对象的,所以如果返回的是 Map 或 User 对象,那么得单独配置一个 Converter,比如 MappingJackson2HttpMessageConverter,这个 Converter 比较强大,能把 String、Map、User 对象等等都能转化成 JSON 格式。
@ComponentScan("com.test")
@Configuration
@EnableWebMvc
public class AppConfig implements WebMvcConfigurer {@Overridepublic void configureMessageConverters(List<HttpMessageConverter<?>> converters) {MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();messageConverter.setDefaultCharset(StandardCharsets.UTF_8);converters.add(messageConverter);}
}
具体转化的逻辑就是 Jackson2 的转化逻辑。

















