返回介绍

11.4 DispatcherServlet 的逻辑处理

发布于 2025-04-22 22:09:17 字数 11592 浏览 0 评论 0 收藏

根据之前的示例,我们知道在 HttpServlet 类中分别提供了相应的服务方法,它们是 doDelete()、doGet()、doOptions()、doPost()、doPut() 和 doTrace(),它会根据请求的不同形式将程序引导至对应的函数进行处理。这几个函数中最常用的函数无非就是 doGet() 和 doPost(),那么我们就直接查看 DispatcherServlet 中对于这两个函数的逻辑实现。

@Override

protected final void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

 processRequest(request, response);

}

@Override

protected final void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

 processRequest(request, response);

}

对于不同的方法,Spring 并没有做特殊处理,而是统一将程序再一次地引导至 process Request(request, response) 中。

protected final void processRequest(HttpServletRequest request, HttpServletResponse

response)

  throws ServletException, IOException {

//记录当前时间,用于计算 web 请求的处理时间

  long startTime = System.currentTimeMillis();

  Throwable failureCause = null;

  // Expose current LocaleResolver and request as LocaleContext.

  LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();

 LocaleContextHolder.setLocaleContext(buildLocaleContext(request),

 this.threadContextInheritable);

  // Expose current RequestAttributes to current thread.

  RequestAttributes previousRequestAttributes = RequestContextHolder. GetRequest

 Attributes();

  ServletRequestAttributes requestAttributes = null;

  if (previousRequestAttributes == null || previousRequestAttributes. getClass().

  equals(ServletRequestAttributes.class)) {

   requestAttributes = new ServletRequestAttributes(request);

  RequestContextHolder.setRequestAttributes(requestAttributes,

  this.threadContextInheritable);

 }

  if (logger.isTraceEnabled()) {

   logger.trace("Bound request context to thread: " + request);

 }

  try {

  doService(request, response);

 }

  catch (ServletException ex) {

   failureCause = ex;

   throw ex;

 }

  catch (IOException ex) {

   failureCause = ex;

   throw ex;

 }

  catch (Throwable ex) {

   failureCause = ex;

   throw new NestedServletException("Request processing failed", ex);

 }

  finally {

   // Clear request attributes and reset thread-bound context.

  LocaleContextHolder.setLocaleContext(previousLocaleContext,this.threadContext

  Inheritable);

   if (requestAttributes != null) {

   RequestContextHolder.setRequestAttributes(previousRequestAttributes,

   this.threadContextInheritable);

   requestAttributes.requestCompleted();

  }

   if (logger.isTraceEnabled()) {

    logger.trace("Cleared thread-bound request context: " + request);

  }

   if (logger.isDebugEnabled()) {

    if (failureCause != null) {

     this.logger.debug("Could not complete request", failureCause);

   }

    else {

     this.logger.debug("Successfully completed request");

   }

  }

   if (this.publishEvents) {

    // Whether or not we succeeded, publish an event.

    long processingTime = System.currentTimeMillis() - startTime;

   this.webApplicationContext.publishEvent(

    new ServletRequestHandledEvent(this,

    request.getRequestURI(), request.getRemoteAddr(),

   request.getMethod(),

   getServletConfig().getServletName(),

   WebUtils.getSessionId(request),

   getUsernameForRequest(request),

    processingTime, failureCause));

  }

 }

}

函数中已经开始了对请求的处理,虽然把细节转移到了 doService 函数中实现,但是我们不难看出处理请求前后所做的准备与处理工作。

(1)为了保证当前线程的 LocaleContext 以及 RequestAttributes 可以在当前请求后还能恢复,提取当前线程的两个属性。

(2)根据当前 request 创建对应的 LocaleContext 和 RequestAttributes,并绑定到当前线程。

(3)委托给 doService 方法进一步处理。

(4)请求处理结束后恢复线程到原始状态。

(5)请求处理结束后无论成功与否发布事件通知。

继续查看 doService 方法。

protected void doService(HttpServletRequest request, HttpServletResponse response) throws

Exception {

  if (logger.isDebugEnabled()) {

   String requestUri = urlPathHelper.getRequestUri(request);

   logger.debug("DispatcherServlet with name '" + getServletName() + "'

   processing " + request.getMethod() +

   " request for [" + requestUri + "]");

 }

  // 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)) {

   logger.debug("Taking snapshot of request attributes before include");

   attributesSnapshot = new HashMap<String, Object>();

   Enumeration<?> attrNames = request.getAttributeNames();

   while (attrNames.hasMoreElements()) {

    String attrName = (String) attrNames.nextElement();

    if (this.cleanupAfterInclude || attrName.startsWith ("org.Springframework.

    web.servlet")) {

     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());

  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 {

   // Restore the original attribute snapshot, in case of an include.

   if (attributesSnapshot != null) {

    restoreAttributesAfterInclude(request, attributesSnapshot);

  }

 }

}

我们猜想对请求处理至少应该包括一些诸如寻找 Handler 并页面跳转之类的逻辑处理,但是,在 doService 中我们并没有看到想看到的逻辑,相反却同样是一些准备工作,但是这些准备工作却是必不可少的。Spring 将已经初始化的功能辅助工具变量,比如 localeResolver、themeResolver 等设置在 request 属性中,而这些属性会在接下来的处理中派上用场。

经过层层的准备工作,终于在 doDispatch 函数中看到了完整的请求处理过程。

protected void doDispatch(HttpServletRequest request, HttpServletResponse response)

throws Exception {

  HttpServletRequest processedRequest = request;

  HandlerExecutionChain mappedHandler = null;

  int interceptorIndex = -1;

  try {

   ModelAndView mv;

   boolean errorView = false;

   try {

 //如果是 MultipartContent 类型的 request 则转换 request 为 MultipartHttpServletRequest 类型的

 request

   processedRequest = checkMultipart(request);

 //根据 request 信息寻找对应的 Handler

   mappedHandler = getHandler(processedRequest, false);

   if (mappedHandler == null || mappedHandler.getHandler() == null) {

   //如果没有找到对应的 handler 则通过 response 反馈错误信息

   noHandlerFound(processedRequest, response);

   return;

  }

  //根据当前的 handler 寻找对应的 HandlerAdapter

   HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

 //如果当前 handler 支持 last-modified 头处理

   String method = request.getMethod();

   boolean isGet = "GET".equals(method);

   if (isGet || "HEAD".equals(method)) {

    long lastModified = ha.getLastModified(request, mappedHandler.

  getHandler());

    if (logger.isDebugEnabled()) {

     String requestUri = urlPathHelper.getRequestUri(request);

     logger.debug("Last-Modified value for [" + requestUri + "] is:

     " + lastModified);

   }

    if (new ServletWebRequest(request, response).checkNotModified

    (lastModified) && isGet) {

    return;

   }

  }

   //拦截器的 preHandler 方法的调用

    HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();

    if (interceptors != null) {

     for (int i = 0; i < interceptors.length; i++) {

      HandlerInterceptor interceptor = interceptors[i];

      if (!interceptor.preHandle(processedRequest, response,

      mappedHandler.getHandler())) {

      triggerAfterCompletion(mappedHandler, interceptorIndex,

       processedRequest, response, null);

      return;

     }

      interceptorIndex = i;

    }

   }

  //真正的激活 handler 并返回视图

    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

   //视图名称转换应用于需要添加前缀后缀的情况

    if (mv != null && !mv.hasView()) {

    mv.setViewName(getDefaultViewName(request));

   }

   //应用所有拦截器的 postHandle 方法

    if (interceptors != null) {

     for (int i = interceptors.length - 1; i >= 0; i--) {

      HandlerInterceptor interceptor = interceptors[i];

     interceptor.postHandle(processedRequest, response, mappedHandler.

     getHandler(), mv);

    }

   }

  }

   catch (ModelAndViewDefiningException ex) {

    logger.debug("ModelAndViewDefiningException encountered", ex);

    mv = ex.getModelAndView();

  }

   catch (Exception ex) {

    Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);

    mv =processHandlerException(processedRequest, response, handler, ex);

    errorView = (mv != null);

  }

   // Did the handler return a view to render?

  //如果在 Handler 实例的处理中返回了 view,那么需要做页面的处理

   if (mv != null && !mv.wasCleared()) {

   //处理页面跳转

   render(mv, processedRequest, response);

    if (errorView) {

    WebUtils.clearErrorRequestAttributes(request);

   }

  }

   else {

    if (logger.isDebugEnabled()) {

     logger.debug("Null ModelAndView returned to DispatcherServlet

     with name '" + getServletName() +

     "': assuming HandlerAdapter completed request handling");

   }

  }

  //完成处理激活触发器

  triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest,

   response, null);

 }

  catch (Exception ex) {

   // Trigger after-completion for thrown exception.

   triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest,

   response, ex);

   throw ex;

 }

  catch (Error err) {

   ServletException ex = new NestedServletException("Handler processing

   failed", err);

   // Trigger after-completion for thrown exception.

   triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest,

   response, ex);

   throw ex;

 }

  finally {

   // Clean up any resources used by a multipart request.

   if (processedRequest != request) {

   cleanupMultipart(processedRequest);

  }

 }

}

doDispatch 函数中展示了 Spring 请求处理所涉及的主要逻辑,而我们之前设置在 request 中的各种辅助属性也都有被派上了用场。下面回顾一下逻辑处理的全过程。

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。