本文将从 DispatcherServlet 收到一个请求开始分析,看 DispatcherServlet 是如何调度这些组件协同完成一个完整的请求的,又是如何去找到我们的自己写的控制器,然后把请求转发给我们的?
我想只要使用过 SpringMvc 的人都知道,我们的请求都是交给 DispatcherServlet 来处理最后再转发到我们的自定义的 Controller 中,说白了 DispatcherServlet 也是一个 Servlet,如果你使用过 Servlet 的话,那么你应该知道客户端的请求都是交给 Servlet 的 service 去处理的,那么我们通过这个信息就可以寻找 DispatcherServlet 的请求入口,从而去跟踪 DispatcherServlet 是怎么去处理好请求,再转交给我们。首先看一下 DispatcherServlet 的类结构继承图:

可以看到 DispatcherServlet 的继承关系还是比较简单的,其中 GenericServlet HttpServlet 是属于 JDK 定义的,首先在 GenericServlet 并未实现 service 方法,而是交给了子类 HttpServlet 实现,而 HttpServlet 在 service 对请求方法,进行了方法方法,根据 GET,POST 等方式,从而转发到 doGet,doPost 等方法。然后子类 FrameworkServlet 重写了 doGet doPost 等方法,然后在这些请求方法里都交给了 processRequest 方法去处理请求,而 processRequest 方法里又转交给了子类 DispatcherServlet 的 doService 方法,在 doService 方法里又调用了 doDispatch 方法,在 doDispatch 方法就真正开始调用组件为我们处理请求了。

上图就是我们的 doDispatch 方法:
getHandler 会去寻找我们的控制器方法,然后寻找与之相匹配的拦截器组成一个 HandlerExecutionChain 对象。preHandle 方法。ModelAndView ,去解析视图并渲染。接下来就重点分析每一步怎么实现的。

this.handlerMappings 集合是 DispatcherServlet 的一个成员变量,里面放的就是所有的 HandlerMapping 对象,那么这些 HandlerMapping 对象是在哪里加载设置的了?是在 initStrategies 方法加载设置进去的,具体的可以看我写的关于 SpringMvc 启动流程。
一般情况下会有三个 HandlerMapping,其中 RequestMappingHandlerMapping 就是用来处理我们的 @RequestMapping 的,我们先不急着分析 RequestMappingHandlerMapping 的 getHandler 怎么执行的。我们先分析 RequestMappingHandlerMapping 是怎么找到我们的 @RequestMapping 类和方法去解析保存的。
RequestMappingHandlerMapping 实现了 InitializingBean 接口,这是 Bean 的生成周期初始化方法,而 RequestMappingHandlerMapping 就是在这个方法里找到我们所有的 Controller。在父类 AbstractHandlerMethodMapping 的 afterPropertiesSet 方法调用了 initHandlerMethods 方法:
protected void initHandlerMethods() {
// 获取容器内所有的 beanName
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
// 解析处理 bean
processCandidateBean(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
首先拿到容器内所有的 BeanName,然后调用 processCandidateBean 挨个遍历判断处理:
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
// 根据beanName 解析出 class 类型
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isTraceEnabled()) {
logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
}
}
// 判断类上是否有 @Controller 或 @RequestMapping 注解
if (beanType != null && isHandler(beanType)) {
// 解析 beanName 所有被 @RequestMapping 修饰的方法
// 然后注册到 mappingRegistry 里面保存,待后面匹配使用
detectHandlerMethods(beanName);
}
}
通过 isHandler 方法判断类上是否有 @Controller 或 RequestMapping 注解来确定这个类是否是一个处理器类,如果是的话调用 detectHandlerMethods 方法:
protected void detectHandlerMethods(Object handler) {
// 判断 handler 是不是一个 String,如果是的话可能是一个 beanName
// 解析出 class 类型
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
// 获取原始类型,主要是针对 CGLIB 这种
Class<?> userType = ClassUtils.getUserClass(handlerType);
// 获取所有 @RequestMapping 注解标注的方法
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));
}
else if (mappingsLogger.isDebugEnabled()) {
mappingsLogger.debug(formatMappings(userType, methods));
}
// 注册找到的方法
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
在 detectHandlerMethods 方法会遍历类中所有的方法,判断是否有 @RequestMapping ,如果有的话会解析注解信息并封装成 RequestMappingInfo 对象,最后遍历所有找到被 @RequestMapping 标注的方法进行注册,会根据路径风格不同,注册到不同的两个容器
/**
* 存放所有处理器方法
* 例如 /ant /ant? /ant{xxx} /ant* /ant**
*/
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
/**
* 存放精确匹配路径的处理器方法
* 例如 /ant
*/
private final MultiValueMap<String, T> pathLookup = new LinkedMultiValueMap<>();
至此我们就完成了寻找处理器方法的任务,并将注解信息都解析好封装成对象保存起来,接下来就可以通过 getHandler 寻找到对应的处理器方法。

首先会跳转到父类 AbstractHandlerMapping 的 getHandler 方法,然后父类会调用子类的 getHandlerInternal 方法真正去获取处理器方法也就是我们用 @RequestMapping 注解标注的方法。
// AbstractHandlerMethodMapping 类的
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();
}
}
接下来就是调用 lookupHandlerMethod 真正根据路径去寻找对应的处理方法。

找到处理器方法以后就会调用 getHandlerExecutionChain 方法将处理器和拦截器组成一个 HandlerExecutionChain 对象返回,那如果没找到对应的处理器方法了?
// 获取处理器链(即控制器+拦截器)
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
// 如果没有找到对应的处理器链对象,则进入404错误处理流程
noHandlerFound(processedRequest, response);
return;
}
但我们拿到对应的处理器方法以后,会调用 getHandlerAdapter 方法为处理器方法寻找对应适配器来去处理请求:

这些处理器适配器也是在 initStrategies 方法加载进去,而 RequestMappingHandlerAdapter 就是用来适配处理 @RequestMapping 注解的。
找到我们的处理器链后,就开始回调拦截器的 preHandle 方法:
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
for (int i = 0; i < this.interceptorList.size(); i++) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
if (!interceptor.preHandle(request, response, this.handler)) {
// 如果有执行失败的,它会为执行过 preHandle 的拦截器,执行 afterCompletion 方法
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
return true;
}
虽然我们大家都知道如果 preHandle 方法返回 false 的话,就不会再往后执行了直接终止,但是这里还有一个比较关键的点就是 triggerAfterCompletion 方法,它会为已经执行过的 preHandle 方法的拦截器执行 afterCompletion 方法:
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
当我们的处理器适配器是 RequestMappingHandlerAdapter 的话,会先调用父类 AbstractHandlerMethodAdapter 的 handle ,转而再调用子类 RequestMappingHandlerAdapter 的 handleInternal 方法:
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
// 检查 session 和 支持的请求方法
checkRequest(request);
// 同步 session, 如果开启的话,同一个 session的请求将被串行处理
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No HttpSession available -> no mutex necessary
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No synchronization on session demanded at all...
mav = invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
接下来我们重点分析 invokeHandlerMethod 方法:
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
// 构建 @InitBinder 工厂
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
// 构建 @ModelAttribute 工厂
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
// 设置方法参数解析器
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
// 设置方法返回值处理器
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
// 设置 @InitBinder 工厂
invocableMethod.setDataBinderFactory(binderFactory);
// 设置参数名解析器
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
// 获取 HttpServletRequest 中存储的 FlashMap
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
// 激活 @ModelAttribute
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
// 异步请求相关处理
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
// 解析参数,调用目标,处理返回值
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
// 生成 ModelAndView
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
在经过前面的一些列处理以后,会调用 invokeAndHandle 方法:
首先看 invokeForRequest 方法去解析参数并调用我们定义的处理器方法:
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 解析参数
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
// 调用目标方法,获取返回值
return doInvoke(args);
}
接着调用 invokeForRequest 去解析请求中的参数,然后解析成我们目标方法所需的参数:

重点看一下 resolveArgument 方法:
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
// 获取参数对应的参数解析器
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
if (resolver == null) {
throw new IllegalArgumentException("Unsupported parameter type [" +
parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
}
// 进行参数解析
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
在 getArgumentResolver 方法会遍历所有的参数解析器,然后调用 supportsParameter 方法看是否能支持解析该参数:

拿到参数解析器对象后就会调用,就会通过这个对象的 resolveArgument 方法去解析对象,我们以 @RequestBody 注解对象参数为例,这种参数类型会被 RequestResponseBodyMethodProcessor 类处理,接下来我们分析在 resolveArgument 方法是怎么处理 @RequestBody 注解参数的,在经过一系列的前置处理以后,最终会调用 AbstractMessageConverterMethodArgumentResolver 类的 readWithMessageConverters 方法进行参数解析转换:

解析完参数以后,最后就会调用 doInvoke 处理器方法获取返回值。
那么你可能会好奇,这么多的参数解析器是在哪里注册的进集合的了?RequestMappingHandlerAdapter 实现了 InitializingBean 接口,在 afterPropertiesSet 方法去注册的参数解析器和返回值解析器的:
public void afterPropertiesSet() {
// 寻找被 @ControllerAdvice 标注的 Bean
initControllerAdviceCache();
//初始化 argumentResolvers,处理参数
if (this.argumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
//初始化 initBinderArgumentResolvers,用于给注释了@initBinder的方法设置参数
if (this.initBinderArgumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
//初始化 argumentResolvers,处理返回值
if (this.returnValueHandlers == null) {
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}
当我们调用 doInvoke 执行完处理器目标方法,就会获得返回值,这时候就会交给我们的返回值解析器去解析,如果你的处理器方法上标注了 @ResponseBody 注解的话,那么就会交给 RequestResponseBodyMethodProcessor 的 handleReturnValue 去处理返回值:
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
// 设置为已经处理过,不会进行视图解析了
mavContainer.setRequestHandled(true);
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
// 寻找合适的 HttpMessageConverter 调用 writer 方法,直接写回响应体
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
最后还要调用 getModelAndView 方法获取一个 ModelAndView 去给后面的流程进行视图解析:
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
modelFactory.updateModel(webRequest, mavContainer);
// 判断这个 Request 是否已经处理过,如果处理过直接返回一个空的ModelAndView
// 类似 RequestResponseBodyMethodProcessor 在 handleReturnValue 处理返回值的时候
// 就会把 requestHandled 设置 为 true,而这个而 RequestResponseBodyMethodProcessor 就是处理 @ResponseBody 注解的
if (mavContainer.isRequestHandled()) {
return null;
}
ModelMap model = mavContainer.getModel();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request != null) {
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
}
return mav;
}
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
throws Exception {
for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
interceptor.postHandle(request, response, this.handler, mv);
}
}
注意这一步不是必须的,如果你是使用 @ReponseBody 注解标注的,返回值都是都直接写会响应体例如返回 Json 字符串,它会返回一个空的 ModelAndView,所以是不需要再进行视图渲染的。视图解析渲染是在 DispatcherServlet 的 processDispatchResult 方法进行的,在分许视图渲染前,先回忆一下,我们要使视图渲染生效,一般都需要配置这样两个 Bean :
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/public/**")
.addResourceLocations("classpath:/public/");
}
@Bean
public InternalResourceViewResolver internalResourceViewResolver(){
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/public/");
resolver.setSuffix(".html");
return resolver;
}
这里我以如下一个控制器方法为例子,并且我的 DispatcherServlet 拦截路径为 / 即拦截所有请求:
@GetMapping("/m1")
public String m1(@RequestParam String name) {
System.out.println("UserController m1" + name);
return "m1";
}
一般来说如果你的返回值是 String,而且你又没有在方法上标注 @ResponseBody 注解的话,会把方法的返回值当成一个视图名称去解析,所以我们的请求最终会流转到 DispatcherServlet 的 resolveViewName 方法,会遍历所有的视图解析器匹配解析:

可以看到最终会被我们自己配置的 InternalResourceViewResolver 解析,然后为 m1 视图名称添加上我们设置的前缀和后缀,之后将会调用 Servlet 的 API 转发请求:
request.getRequestDispatcher(url)forward(request, response);
由于我设置的 DispatcherServlet 拦截路径为 / 即拦截所有请求,其实这个请求最终又会流转到 DispatcherServlet 来处理,这时候我们配置的 addResourceHandlers 就生效了,因为它会匹配到这个路径,然后去你配置的 ResourceLocations 找到这个静态资源返回。

可以看到我们的静态资源请求确实又重定向到 DispatcherServlet 这里来处理,最终会被 SimpleUrlHandlerMapping 这个类处理,而我们添加了一个 ResourceHandler 其实就是向 SimpleUrlHandlerMapping 这个类注册了一个可以匹配的 url。
Spring — Sep 20, 2022