SpringMVC源码解析---doDispatch

1.概述

先提个问题:当浏览器发起http请求,SpringMVC是如何拦截的?又是如何处理请求并响应给用户的呢?

不清楚的读者可以接着往下看。

SpringMVC是基于Servlet实现的。有一个重要的组件就是DispatcherServlet,在配置文件中可以配置它可以拦截的一些请求,如下所示,表示拦截所有的请求。

在这里插入图片描述

熟悉Servlet的都清楚,拦截到请求后会执行doService方法,在DispatcherServlet当中,doService的实现如下:

@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
   logRequest(request);

   // Keep a snapshot of the request attributes in case of an include,
   // to be able to restore the original attributes after the include.
   Map<String, Object> attributesSnapshot = null;
   if (WebUtils.isIncludeRequest(request)) {
      attributesSnapshot = new HashMap<>();
      Enumeration<?> attrNames = request.getAttributeNames();
      while (attrNames.hasMoreElements()) {
         String attrName = (String) attrNames.nextElement();
         if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
            attributesSnapshot.put(attrName, request.getAttribute(attrName));
         }
      }
   }

   // Make framework objects available to handlers and view objects.
   request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
   request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
   request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
   request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

   if (this.flashMapManager != null) {
      FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
      if (inputFlashMap != null) {
         request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
      }
      request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
      request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
   }

   try {
      /**
       * 真正的实现分发请求
       */
      doDispatch(request, response);
   }
   finally {
      if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
         // Restore the original attribute snapshot, in case of an include.
         if (attributesSnapshot != null) {
            restoreAttributesAfterInclude(request, attributesSnapshot);
         }
      }
   }
}

最重要的就是会调用doDispatch方法,这个方法里就包括了如何处理接收到的请求,执行指定的方法逻辑,并返回给用户。下面将对doDispatch方法执行逻辑进行分析。

2.doDispatch

下图展示了doDispatch方法执行的流程

在这里插入图片描述

跟着源码来看看,每一步都做了什么以及为什么要这样做。

2.1 getHandler

springMVC当中提供了4种类型的Handler来处理request请求。4中类型分别如下所示:

  1. @RequestMapping 也就是我们开发中经常使用的

  2. 传统的Servlet

  3. 实现了Controller的类

    例如

    @Component("/senlin")
    public class ControllerHandler implements Controller {
        @Override
        public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) {
            System.out.println(1);
            ModelAndView userView = new ModelAndView("userView2");//viewResolver
            userView.addObject("name","森林");
            return userView;
        }
    }
    
  4. 实现了HttpRequestHandler的类

    例如

    @Component("/*/senlin")
    public class SimpleHandler implements HttpRequestHandler {
        @Override
        public void handleRequest(HttpServletRequest request,
                                  HttpServletResponse response) throws ServletException, IOException {
            response.getWriter().write("森林大帅哥");
        }
    }
    

所以首先需要根据request来获取指定的Handler来处理当前请求,调用getHandler方法,getHandler源码如下所示

// DispatcherServlet
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
   if (this.handlerMappings != null) {
      for (HandlerMapping mapping : this.handlerMappings) {//遍历handlerMappings
         HandlerExecutionChain handler = mapping.getHandler(request);
         if (handler != null) {
            return handler;
         }
      }
   }
   return null;
}

遍历handlerMappings,handlerMapping是指不同类型Handler的映射的集合。handlerMapping类图如下所示

在这里插入图片描述

例如BeanNameUrlHandlerMapping继承了AbstractUrlHandlerMapping。这个Mapping中维护了一个散列表handlerMap。url作为key,handler作为value,如下图所示

在这里插入图片描述

回到上面的方法,遍历每一次都会调用 AbstractHandlerMapping中的getHandler方法,源码如下:

@Override
@Nullable
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);//1.从容器中根据名字获取Bean
   }

   HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);//2.有了Bean并不是直接返回,而是要根据当前的请求看看有没有拦截器会拦截它,得到一个执行器链

   if (logger.isTraceEnabled()) {
      logger.trace("Mapped to " + handler);
   }
   else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
      logger.debug("Mapped to " + executionChain.getHandler());
   }

   if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
      CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
      CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
      config = (config != null ? config.combine(handlerConfig) : handlerConfig);
      executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
   }

   return executionChain;
}

该方法并不仅仅只是根据request来得到对应的handler,还需要根据当前请求,判断是否有拦截器会拦截,得到一个拦截器链。将handler和拦截器链封装到HandlerExecutionChain当中。

2.2 getHandlerAdapter

由于SpringMVC提供了4种类型的Handler,并且他们没有统一的调用接口,所以需要通过适配器来使用。同样适配器的类型也有4种。如下所示:

在这里插入图片描述

调用DispatcherServlet中的getHandlerAdapter,源码如下:

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

遍历这4种适配器,找到支持当前Handler类型的适配器并返回。

2.3 applyPreHandle

该方法会执行HandlerInterceptor 中的preHandle方法,执行拦截器的前置处理

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
   HandlerInterceptor[] interceptors = getInterceptors();
   if (!ObjectUtils.isEmpty(interceptors)) {
      for (int i = 0; i < interceptors.length; i++) {
         HandlerInterceptor interceptor = interceptors[i];
         if (!interceptor.preHandle(request, response, this.handler)) {
            triggerAfterCompletion(request, response, null);
            return false;
         }
         this.interceptorIndex = i;
      }
   }
   return true;
}

遍历之前根据request找到所有拦截器,执行拦截器的preHandle。如果此时拦截器执行结果不放行当前的请求,则不再进行下面的逻辑。

2.4 handle

通过适配器,调用handle执行真正的请求处理逻辑,并返回modelAndView。这一部分有4种handler,而这4种handler的执行的方式不同,参数处理和结果集处理的种类也很多。所以这里先清楚是在什么时候执行的请求逻辑即可。

2.5 applyDefaultViewName

上一步返回的modelAndVIew中 如果对象视图为空,则此时会设置默认的对象视图名字到MV当中

private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception {
   if (mv != null && !mv.hasView()) {
      String defaultViewName = getDefaultViewName(request);
      if (defaultViewName != null) {
         mv.setViewName(defaultViewName);
      }
   }
}
2.6 applyPostHandle

之前有拦截器的前置处理,那么必定会有拦截器的后置处理,此时会遍历关联的拦截器,挨个的执行postHandle方法。

void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
      throws Exception {

   HandlerInterceptor[] interceptors = getInterceptors();
   if (!ObjectUtils.isEmpty(interceptors)) {
      for (int i = interceptors.length - 1; i >= 0; i--) {
         HandlerInterceptor interceptor = interceptors[i];
         interceptor.postHandle(request, response, this.handler, mv);
      }
   }
}
2.7 processDispatchResult

请求解析视图并分发结果,调用processDispatchResult。若有异常,则会调用异常处理,返回一个异常视图,接着就会执行render方法进行解析视图并分发结果.最后执行拦截器链的完成处理方法。

   private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
         @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
         @Nullable Exception exception) throws Exception {
   
      boolean errorView = false;
   
      if (exception != null) {//异常视图处理
         if (exception instanceof ModelAndViewDefiningException) {
            logger.debug("ModelAndViewDefiningException encountered", exception);
            mv = ((ModelAndViewDefiningException) exception).getModelAndView();
         }
         else {
            Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
            mv = processHandlerException(request, response, handler, exception);
            errorView = (mv != null);
         }
      }
   
      // Did the handler return a view to render?
      if (mv != null && !mv.wasCleared()) {
         render(mv, request, response);//解析视图 分发结果
         if (errorView) {
            WebUtils.clearErrorRequestAttributes(request);
         }
      }
      else {
         if (logger.isTraceEnabled()) {
            logger.trace("No view rendering, null ModelAndView returned.");
         }
      }
   
      if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
         // Concurrent handling started during a forward
         return;
      }
   
      if (mappedHandler != null) {
         // Exception (if any) is already handled..
         mappedHandler.triggerAfterCompletion(request, response, null);//触发拦截器完成处理
      }
   }

基于视图名,遍历所有的ViewResolver,找到符合的ViewResolver进行解析,得到View对象。在这里插入图片描述

如果每次请求都需要去创建解析视图的话,那么想必性能是会受到影响的。所以ViewResolver有缓存的功能,首先会走缓存,缓存中去寻找视图,有的话直接返回。否则才会去解析创建视图,创建好之后放入到缓存当中。缓存是使用LinkedHashMap来实现的,并设置了accessOrder为True。采用最近最少使用的溢出淘汰机制。也就是每次访问过的,会放到链表的最前面,当达到溢出条件的时候,会将最后面的视图删除。

  public View resolveViewName(String viewName, Locale locale) throws Exception {
     if (!isCache()) {
        return createView(viewName, locale);
     }
     else {
        Object cacheKey = getCacheKey(viewName, locale);
        View view = this.viewAccessCache.get(cacheKey);//缓存中去寻找视图
        if (view == null) {//为空 解析创建视图
           synchronized (this.viewCreationCache) {
              view = this.viewCreationCache.get(cacheKey);
              if (view == null) {
                 // Ask the subclass to create the View object.
                 view = createView(viewName, locale);
                 if (view == null && this.cacheUnresolved) {
                    view = UNRESOLVED_VIEW;
                 }
                 if (view != null && this.cacheFilter.filter(view, viewName, locale)) {
                   //放入缓存
                    this.viewAccessCache.put(cacheKey, view);
                    this.viewCreationCache.put(cacheKey, view);
                 }
              }
           }
        }
        else {
           if (logger.isTraceEnabled()) {
              logger.trace(formatKey(cacheKey) + "served from cache");
           }
        }
        return (view != UNRESOLVED_VIEW ? view : null);
     }
  }

3.后续

这篇博客主要介绍了DispatcherServlet的核心流程,具体的每一块的细节将在后面的博客中展开

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值