🌑

Shawn Fux

从源码角度分析DispatcherServlet请求过程

本文将从 DispatcherServlet 收到一个请求开始分析,看 DispatcherServlet 是如何调度这些组件协同完成一个完整的请求的,又是如何去找到我们的自己写的控制器,然后把请求转发给我们的?

DispatcherServlet入口

我想只要使用过 SpringMvc 的人都知道,我们的请求都是交给 DispatcherServlet 来处理最后再转发到我们的自定义的 Controller 中,说白了 DispatcherServlet 也是一个 Servlet,如果你使用过 Servlet 的话,那么你应该知道客户端的请求都是交给 Servlet 的 service 去处理的,那么我们通过这个信息就可以寻找 DispatcherServlet 的请求入口,从而去跟踪 DispatcherServlet 是怎么去处理好请求,再转交给我们。首先看一下 DispatcherServlet 的类结构继承图:

DispatcherServlet

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

doDispatch

上图就是我们的 doDispatch 方法:

  • 第一步:首先会调用 getHandler 会去寻找我们的控制器方法,然后寻找与之相匹配的拦截器组成一个 HandlerExecutionChain 对象。
  • 第二步:得到处理器后,会寻找对应的处理器适配器。
  • 第三步:调用拦截器的 preHandle 方法。
  • 第四步:解析参数调用目标方法(即我们定义的处理器方法),然后获取解析目标方法的返回值。
  • 第五步:通过第四步返回的 ModelAndView ,去解析视图并渲染。

接下来就重点分析每一步怎么实现的。

获取处理器链

handlerMappings

this.handlerMappings 集合是 DispatcherServlet 的一个成员变量,里面放的就是所有的 HandlerMapping 对象,那么这些 HandlerMapping 对象是在哪里加载设置的了?是在 initStrategies 方法加载设置进去的,具体的可以看我写的关于 SpringMvc 启动流程

一般情况下会有三个 HandlerMapping,其中 RequestMappingHandlerMapping 就是用来处理我们的 @RequestMapping 的,我们先不急着分析 RequestMappingHandlerMappinggetHandler 怎么执行的。我们先分析 RequestMappingHandlerMapping 是怎么找到我们的 @RequestMapping 类和方法去解析保存的。

解析 @RequestMapping

RequestMappingHandlerMapping 实现了 InitializingBean 接口,这是 Bean 的生成周期初始化方法,而 RequestMappingHandlerMapping 就是在这个方法里找到我们所有的 Controller。在父类 AbstractHandlerMethodMappingafterPropertiesSet 方法调用了 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 方法判断类上是否有 @ControllerRequestMapping 注解来确定这个类是否是一个处理器类,如果是的话调用 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 寻找到对应的处理器方法。

寻找处理器方法

getHandler

首先会跳转到父类 AbstractHandlerMappinggetHandler 方法,然后父类会调用子类的 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 真正根据路径去寻找对应的处理方法。

lookupHandlerMethod

找到处理器方法以后就会调用 getHandlerExecutionChain 方法将处理器和拦截器组成一个 HandlerExecutionChain 对象返回,那如果没找到对应的处理器方法了?

// 获取处理器链(即控制器+拦截器)
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
   // 如果没有找到对应的处理器链对象,则进入404错误处理流程
   noHandlerFound(processedRequest, response);
   return;
}

获取处理器适配器

但我们拿到对应的处理器方法以后,会调用 getHandlerAdapter 方法为处理器方法寻找对应适配器来去处理请求:

getHandlerAdapter

这些处理器适配器也是在 initStrategies 方法加载进去,而 RequestMappingHandlerAdapter 就是用来适配处理 @RequestMapping 注解的。

拦截器preHandle方法

找到我们的处理器链后,就开始回调拦截器的 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 的话,会先调用父类 AbstractHandlerMethodAdapterhandle ,转而再调用子类 RequestMappingHandlerAdapterhandleInternal 方法:

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 方法: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 去解析请求中的参数,然后解析成我们目标方法所需的参数:

getMethodArgumentValues

重点看一下 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

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

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 注解的话,那么就会交给 RequestResponseBodyMethodProcessorhandleReturnValue 去处理返回值:

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;
}

拦截器postHandle方法

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,所以是不需要再进行视图渲染的。视图解析渲染是在 DispatcherServletprocessDispatchResult 方法进行的,在分许视图渲染前,先回忆一下,我们要使视图渲染生效,一般都需要配置这样两个 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 注解的话,会把方法的返回值当成一个视图名称去解析,所以我们的请求最终会流转到 DispatcherServletresolveViewName 方法,会遍历所有的视图解析器匹配解析:

resolveViewName

可以看到最终会被我们自己配置的 InternalResourceViewResolver 解析,然后为 m1 视图名称添加上我们设置的前缀和后缀,之后将会调用 Servlet 的 API 转发请求:

request.getRequestDispatcher(url)forward(request, response);

由于我设置的 DispatcherServlet 拦截路径为 / 即拦截所有请求,其实这个请求最终又会流转到 DispatcherServlet 来处理,这时候我们配置的 addResourceHandlers 就生效了,因为它会匹配到这个路径,然后去你配置的 ResourceLocations 找到这个静态资源返回。

Foward

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

— Sep 20, 2022