亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

SpringMVC源碼分析之什么是FrameworkServlet

發布時間:2021-10-15 13:46:36 來源:億速云 閱讀:126 作者:iii 欄目:開發技術

本篇內容介紹了“SpringMVC源碼分析之什么是FrameworkServlet”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

1.FrameworkServlet

FrameworkServlet 繼承自 HttpServletBean,而 HttpServletBean 繼承自  HttpServlet,HttpServlet 就是 JavaEE 里邊的東西了,這里我們不做討論,從 HttpServletBean  開始就是框架的東西了,但是 HttpServletBean  比較特殊,它的特殊在于它沒有進行任何的請求處理,只是參與了一些初始化的操作,這些比較簡單,而且我們在上篇文章中也已經分析過了,所以這里我們對  HttpServletBean 不做分析,就直接從它的子類 FrameworkServlet 開始看起。

SpringMVC源碼分析之什么是FrameworkServlet

和所有的 Servlet 一樣,FrameworkServlet 對請求的處理也是從 service 方法開始,我們先來看看該方法  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 進行處理。

然而在 HttpServlet 中并未對 doGet、doPost 等請求進行實質性處理,所以 FrameworkServlet  中還重寫了各種請求對應的方法,如 doDelete、doGet、doOptions、doPost、doPut、doTrace 等,其實就是除了 doHead  之外的其他方法都重寫了。

我們先來看看 doDelete、doGet、doPost 以及 doPut 四個方法:

@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); } @Override protected final void doPut(HttpServletRequest request, HttpServletResponse response)   throws ServletException, IOException {  processRequest(request, response); } @Override protected final void doDelete(HttpServletRequest request, HttpServletResponse response)   throws ServletException, IOException {  processRequest(request, response); }

可以看到,這里又把請求交給 processRequest 去處理了,在 processRequest 方法中則會進一步調用到  doService,對不同類型的請求分類處理。

doOptions 和 doTrace 則稍微有些差異,如下:

@Override protected void doOptions(HttpServletRequest request, HttpServletResponse response)   throws ServletException, IOException {  if (this.dispatchOptionsRequest || CorsUtils.isPreFlightRequest(request)) {   processRequest(request, response);   if (response.containsHeader("Allow")) {    return;   }  }  super.doOptions(request, new HttpServletResponseWrapper(response) {   @Override   public void setHeader(String name, String value) {    if ("Allow".equals(name)) {     value = (StringUtils.hasLength(value) ? value + ", " : "") + HttpMethod.PATCH.name();    }    super.setHeader(name, value);   }  }); } @Override protected void doTrace(HttpServletRequest request, HttpServletResponse response)   throws ServletException, IOException {  if (this.dispatchTraceRequest) {   processRequest(request, response);   if ("message/http".equals(response.getContentType())) {    return;   }  }  super.doTrace(request, response); }

可以看到這兩個方法的處理多了一層邏輯,就是去選擇是在當前方法中處理對應的請求還是交給父類去處理,由于 dispatchOptionsRequest 和  dispatchTraceRequest 變量默認都是 false,因此默認情況下,這兩種類型的請求都是交給了父類去處理。

2.processRequest

我們再來看 processRequest,這算是 FrameworkServlet 的核心方法了:

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

這個方法雖然比較長,但是其實它的核心就是最中間的 doService 方法,以 doService 為界,我們可以將該方法的內容分為三部分:

  1. 鴻蒙官方戰略合作共建——HarmonyOS技術社區

  2. doService 之前主要是一些準備工作,準備工作主要干了兩件事,第一件事就是從 LocaleContextHolder 和  RequestContextHolder 中分別獲取它們原來保存的 LocaleContext 和 RequestAttributes 對象存起來,然后分別調用  buildLocaleContext 和 buildRequestAttributes 方法獲取到當前請求的 LocaleContext 和  RequestAttributes 對象,再通過 initContextHolders 方法將當前請求的 LocaleContext 和  RequestAttributes 對象分別設置到 LocaleContextHolder 和 RequestContextHolder  對象中;第二件事則是獲取到異步管理器并設置攔截器。

  3. 接下來就是 doService 方法,這是一個抽象方法,具體的實現在 DispatcherServlet 中,這個松哥放到  DispatcherServlet 中再和大家分析。

  4. 第三部分就是 finally 中,這個里邊干了兩件事:第一件事就是將 LocaleContextHolder 和 RequestContextHolder  中對應的對象恢復成原來的樣子(參考第一步);第二件事就是通過 publishRequestHandledEvent 方法發布一個  ServletRequestHandledEvent 類型的消息。

經過上面的分析,大家發現,processRequest 其實主要做了兩件事,第一件事就是對 LocaleContext 和  RequestAttributes 的處理,第二件事就是發布事件。我們對這兩件事分別來研究。

2.1 LocaleContext 和 RequestAttributes

LocaleContext 和 RequestAttributes 都是接口,不同的是里邊存放的對象不同。

2.1.1 LocaleContext

LocaleContext 里邊存放著 Locale,也就是本地化信息,如果我們需要支持國際化,就會用到 Locale。

國際化的時候,如果我們需要用到 Locale 對象,第一反應就是從 HttpServletRequest 中獲取,像下面這樣:

Locale locale = req.getLocale();

但是大家知道,HttpServletRequest 只存在于 Controller 中,如果我們想要在 Service 層獲取  HttpServletRequest,就得從 Controller 中傳參數過來,這樣就比較麻煩,特別是有的時候 Service  中相關方法都已經定義好了再去修改,就更頭大了。

所以 SpringMVC 中還給我們提供了 LocaleContextHolder,這個工具就是用來保存當前請求的 LocaleContext  的。當大家看到 LocaleContextHolder 時不知道有沒有覺得眼熟,松哥在之前的 Spring Security 系列教程中和大家聊過  SecurityContextHolder,這兩個的原理基本一致,都是基于 ThreadLocal 來保存變量,進而確保不同線程之間互不干擾,對  ThreadLocal 不熟悉的小伙伴,可以看看松哥的 Spring Security 系列,之前有詳細分析過(公號后臺回復 ss)。

有了 LocaleContextHolder 之后,我們就可以在任何地方獲取 Locale 了,例如在 Service 中我們可以通過如下方式獲取  Locale:

Locale locale = LocaleContextHolder.getLocale();

上面這個 Locale 對象實際上就是從 LocaleContextHolder 中的 LocaleContext 里邊取出來的。

需要注意的是,SpringMVC 中還有一個 LocaleResolver 解析器,所以前面 req.getLocale() 并不總是獲取到 Locale  的值,這個松哥在以后的文章中再和小伙伴們細聊。

2.1.2 RequestAttributes

RequestAttributes 是一個接口,這個接口可以用來 get/set/remove 某一個屬性。

RequestAttributes 有諸多實現類,默認使用的是 ServletRequestAttributes,通過  ServletRequestAttributes,我們可以 getRequest、getResponse 以及 getSession。

在 ServletRequestAttributes 的具體實現中,會通過 scope 參數判斷操作 request 還是操作  session(如果小伙伴們不記得 Spring 中的作用域問題,可以公號后臺回復 spring,看看松哥錄制的免費的 Spring  入門教程,里邊有講),我們來看一下 ServletRequestAttributes#setAttribute 方法(get/remove  方法執行邏輯類似):

public void setAttribute(String name, Object value, int scope) {     if (scope == 0) {         if (!this.isRequestActive()) {             throw new IllegalStateException("Cannot set request attribute - request is not active anymore!");         }         this.request.setAttribute(name, value);     } else {         HttpSession session = this.obtainSession();         this.sessionAttributesToUpdate.remove(name);         session.setAttribute(name, value);     } }

可以看到,這里會先判斷 scope,scope 為 0 就操作 request,scope 為 1 就操作 session。如果操作的是  request,則需要首先通過 isRequestActive 方法判斷當前 request 是否執行完畢,如果執行完畢,就不可以再對其進行其他操作了(當執行了  finally 代碼塊中的 requestAttributes.requestCompleted 方法后,isRequestActive 就會返回  false)。

和 LocaleContext 類似,RequestAttributes 被保存在 RequestContextHolder  中,RequestContextHolder 的原理也和 SecurityContextHolder 類似,這里不再贅述。

看了上面的講解,大家應該發現了,在 SpringMVC 中,如果我們需要在 Controller 之外的其他地方使用 request、response  以及 session,其實不用每次都從 Controller 中傳遞 request、response 以及 session 等對象,我們完全可以直接通過  RequestContextHolder 來獲取,像下面這樣:

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

是不是非常 easy!

2.2 事件發布

最后就是 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()));  } }

可以看到,事件的發送需要 publishEvents 為 true,而該變量默認就是 true。如果需要修改該變量的值,可以在 web.xml 中配置  DispatcherServlet 時,通過 init-param  節點順便配置一下該變量的值。正常情況下,這個事件總是會被發送出去,如果項目有需要,我們可以監聽該事件,如下:

@Component public class ServletRequestHandleListener implements ApplicationListener<ServletRequestHandledEvent> {     @Override     public void onApplicationEvent(ServletRequestHandledEvent servletRequestHandledEvent) {         System.out.println("請求執行完畢-"+servletRequestHandledEvent.getRequestUrl());     } }

當一個請求執行完畢時,該事件就會被觸發。

“SpringMVC源碼分析之什么是FrameworkServlet”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

武汉市| 桐乡市| 罗平县| 崇义县| 涟源市| 泽州县| 利川市| 雷波县| 绥德县| 衡阳县| 郧西县| 新宾| 佛冈县| 阳曲县| 阿克苏市| 丘北县| 确山县| 两当县| 蓬莱市| 华坪县| 新巴尔虎左旗| 武清区| 平南县| 拜城县| 柘荣县| 隆安县| 定西市| 江口县| 晋中市| 长白| 行唐县| 河津市| 潞西市| 三门峡市| 高尔夫| 盐津县| 漯河市| 垣曲县| 遵义县| 南江县| 东海县|