美文网首页
[Spring源码]FrameworkServlet

[Spring源码]FrameworkServlet

作者: 檀香灰 | 来源:发表于2021-03-23 16:48 被阅读0次

    一.FrameworkServlet 主要介绍

    Spring MVC 的核心是DispatcherServlet,其父类是FramworkServlet.

    1.1 继承关系

    1.png
    • HttpServletBean 继承自HttpServlet
    • FrameworkServlet 的核心方法是 service
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
     HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
     if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
      processRequest(request, response);
     }
     else {
      super.service(request, response);
     }
    }
    

    说明:

    • 主要是对PATCH 方法做了特殊处理,其它由 super.service 处理。

    1.2 FrameworkServlet

    HttpServlet 中并未对 doGet/doPost 等进行实质性处理,所以FrameworkServlet 中重写了各种请求对应的方法,如:doDelete、doGet、doOptions、doPost、doPut、doTrace 等,其实就是除了 doHead 之外的其他方法都重写了。

    1.3 FrameworkServlet 核心方法 processRequest

    方法如下:

    protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
     long startTime = System.currentTimeMillis();
     Throwable failureCause = null;
     LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
     LocaleContext localeContext = buildLocaleContext(request);
     RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
     ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
     WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
     asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
     initContextHolders(request, localeContext, requestAttributes);
     try {
      doService(request, response);
     }
     catch (ServletException | IOException ex) {
      failureCause = ex;
      throw ex;
     }
     catch (Throwable ex) {
      failureCause = ex;
      throw new NestedServletException("Request processing failed", ex);
     }
     finally {
      resetContextHolders(request, previousLocaleContext, previousAttributes);
      if (requestAttributes != null) {
       requestAttributes.requestCompleted();
      }
      logResult(request, response, failureCause, asyncManager);
      publishRequestHandledEvent(request, response, startTime, failureCause);
     }
    }
    

    processRequest 其实主要做了两件事,第一件事就是对 LocaleContext 和 RequestAttributes 的处理,第二件事就是发布事件。方法分为三部分:
    1.doService 之前主要是一些准备工作,准备工作主要干了两件事:

    第一件事就是从 LocaleContextHolder 和 RequestContextHolder 中分别获取它们原来保存的 LocaleContext 和 RequestAttributes 对象存起来,然后分别调用 buildLocaleContext 和 buildRequestAttributes 方法获取到当前请求的 LocaleContext 和 RequestAttributes 对象,再通过 initContextHolders 方法将当前请求的 LocaleContext 和 RequestAttributes 对象分别设置到 LocaleContextHolder 和 RequestContextHolder 对象中;

    第二件事则是获取到异步管理器并设置拦截器。

    2.接下来就是 doService 方法,这是一个抽象方法,具体的实现在 DispatcherServlet 中
    3.最后就是 finally 中,这个里边干了两件事

    第一件事就是将 LocaleContextHolder 和 RequestContextHolder 中对应的对象恢复成原来的样子(参考第一步);

    第二件事就是通过 publishRequestHandledEvent 方法发布一个 ServletRequestHandledEvent 类型的消息。

    1.3 processRequest 的事件发布

    finally 代码块中会调用 publishRequestHandledEvent 方法发送一个 ServletRequestHandledEvent 类型的事件。

    private void publishRequestHandledEvent(HttpServletRequest request, HttpServletResponse response,
      long startTime, @Nullable Throwable failureCause) {
     if (this.publishEvents && this.webApplicationContext != null) {
      // 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, response.getStatus()));
     }
    }
    

    二.LocalContext 和 RequestAttributes

    2.1 LocalContext

    LocaleContext 里面存放着Locale ,即本地化信息.
    国际化的时候,如果需要用到Locale 对象,第一反应是从HttpServletRequest 中获取,类似:

    Locale locale = req.getLocale();
    
    但是,HttpServletRequest 只存放于Controller 中,如果想要在Service 层获取,就得cong Controller 中传参数过来,比较麻烦。
    

    Spring MVC 提供了 LocaleContextHolder, 这个工具就是用来保存当前请求的LocaleContext, 是基于ThreadLocal 来保存变量,进而确保不同线程之间互不干扰。有了LocaleContextHolder 之后,就可以在任何地方获取Locale 了:

    Locale locale = LocaleContextHolder.getLocale();
    

    2.2 RequestAttributes

    RequestAttributes 是一个接口,这个接口可以用来 get/set/remove 某一个属性。
    RequestAttributes 有诸多实现类,默认使用的是 ServletRequestAttributes,通过 ServletRequestAttributes,我们可以 getRequest、getResponse 以及 getSession。
    

    和 LocaleContext 类似,RequestAttributes 被保存在 RequestContextHolder
    在 SpringMVC 中,如果我们需要在 Controller 之外的其他地方使用 request、response 以及 session,其实不用每次都从 Controller 中传递 request、response 以及 session 等对象,我们完全可以直接通过 RequestContextHolder 来获取:

    ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    HttpServletRequest request = servletRequestAttributes.getRequest();
    HttpServletResponse response = servletRequestAttributes.getResponse();
    

    相关文章

      网友评论

          本文标题:[Spring源码]FrameworkServlet

          本文链接:https://www.haomeiwen.com/subject/oavfhltx.html