成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

SpringMVC 源碼分析之 FrameworkServlet

開(kāi)發(fā) 架構(gòu)
很多小伙伴都知道 SpringMVC 的核心是 DispatcherServlet,而 DispatcherServlet 的父類(lèi)就是 FrameworkServlet,因此我們先來(lái)看看 FrameworkServlet,這有助于我們理解 DispatcherServlet。

[[389076]]

前面和小伙伴們聊了 SpringMVC 的初始化流程,相信大家對(duì)于 SpringMVC 的初始化過(guò)程都有一個(gè)基本認(rèn)知了,今天我們就來(lái)看看當(dāng)一個(gè)請(qǐng)求到達(dá)后,它的執(zhí)行流程是什么樣的?當(dāng)然這個(gè)流程比較長(zhǎng),松哥這里可能會(huì)分兩篇文章來(lái)和大家分享。

很多小伙伴都知道 SpringMVC 的核心是 DispatcherServlet,而 DispatcherServlet 的父類(lèi)就是 FrameworkServlet,因此我們先來(lái)看看 FrameworkServlet,這有助于我們理解 DispatcherServlet。

1.FrameworkServlet

FrameworkServlet 繼承自 HttpServletBean,而 HttpServletBean 繼承自 HttpServlet,HttpServlet 就是 JavaEE 里邊的東西了,這里我們不做討論,從 HttpServletBean 開(kāi)始就是框架的東西了,但是 HttpServletBean 比較特殊,它的特殊在于它沒(méi)有進(jìn)行任何的請(qǐng)求處理,只是參與了一些初始化的操作,這些比較簡(jiǎn)單,而且我們?cè)谏掀恼轮幸惨呀?jīng)分析過(guò)了,所以這里我們對(duì) HttpServletBean 不做分析,就直接從它的子類(lèi) FrameworkServlet 開(kāi)始看起。

和所有的 Servlet 一樣,F(xiàn)rameworkServlet 對(duì)請(qǐng)求的處理也是從 service 方法開(kāi)始,我們先來(lái)看看該方法 FrameworkServlet#service:

  1. @Override 
  2. protected void service(HttpServletRequest request, HttpServletResponse response) 
  3.   throws ServletException, IOException { 
  4.  HttpMethod httpMethod = HttpMethod.resolve(request.getMethod()); 
  5.  if (httpMethod == HttpMethod.PATCH || httpMethod == null) { 
  6.   processRequest(request, response); 
  7.  } 
  8.  else { 
  9.   super.service(request, response); 
  10.  } 

可以看到,在該方法中,首先獲取到當(dāng)前請(qǐng)求方法,然后對(duì) patch 請(qǐng)求額外關(guān)照了下,其他類(lèi)型的請(qǐng)求統(tǒng)統(tǒng)都是 super.service 進(jìn)行處理。

然而在 HttpServlet 中并未對(duì) doGet、doPost 等請(qǐng)求進(jìn)行實(shí)質(zhì)性處理,所以 FrameworkServlet 中還重寫(xiě)了各種請(qǐng)求對(duì)應(yīng)的方法,如 doDelete、doGet、doOptions、doPost、doPut、doTrace 等,其實(shí)就是除了 doHead 之外的其他方法都重寫(xiě)了。

我們先來(lái)看看 doDelete、doGet、doPost 以及 doPut 四個(gè)方法:

  1. @Override 
  2. protected final void doGet(HttpServletRequest request, HttpServletResponse response) 
  3.   throws ServletException, IOException { 
  4.  processRequest(request, response); 
  5. @Override 
  6. protected final void doPost(HttpServletRequest request, HttpServletResponse response) 
  7.   throws ServletException, IOException { 
  8.  processRequest(request, response); 
  9. @Override 
  10. protected final void doPut(HttpServletRequest request, HttpServletResponse response) 
  11.   throws ServletException, IOException { 
  12.  processRequest(request, response); 
  13. @Override 
  14. protected final void doDelete(HttpServletRequest request, HttpServletResponse response) 
  15.   throws ServletException, IOException { 
  16.  processRequest(request, response); 

可以看到,這里又把請(qǐng)求交給 processRequest 去處理了,在 processRequest 方法中則會(huì)進(jìn)一步調(diào)用到 doService,對(duì)不同類(lèi)型的請(qǐng)求分類(lèi)處理。

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

  1. @Override 
  2. protected void doOptions(HttpServletRequest request, HttpServletResponse response) 
  3.   throws ServletException, IOException { 
  4.  if (this.dispatchOptionsRequest || CorsUtils.isPreFlightRequest(request)) { 
  5.   processRequest(request, response); 
  6.   if (response.containsHeader("Allow")) { 
  7.    return
  8.   } 
  9.  } 
  10.  super.doOptions(request, new HttpServletResponseWrapper(response) { 
  11.   @Override 
  12.   public void setHeader(String name, String value) { 
  13.    if ("Allow".equals(name)) { 
  14.     value = (StringUtils.hasLength(value) ? value + ", " : "") + HttpMethod.PATCH.name(); 
  15.    } 
  16.    super.setHeader(name, value); 
  17.   } 
  18.  }); 
  19. @Override 
  20. protected void doTrace(HttpServletRequest request, HttpServletResponse response) 
  21.   throws ServletException, IOException { 
  22.  if (this.dispatchTraceRequest) { 
  23.   processRequest(request, response); 
  24.   if ("message/http".equals(response.getContentType())) { 
  25.    return
  26.   } 
  27.  } 
  28.  super.doTrace(request, response); 

可以看到這兩個(gè)方法的處理多了一層邏輯,就是去選擇是在當(dāng)前方法中處理對(duì)應(yīng)的請(qǐng)求還是交給父類(lèi)去處理,由于 dispatchOptionsRequest 和 dispatchTraceRequest 變量默認(rèn)都是 false,因此默認(rèn)情況下,這兩種類(lèi)型的請(qǐng)求都是交給了父類(lèi)去處理。

2.processRequest

我們?cè)賮?lái)看 processRequest,這算是 FrameworkServlet 的核心方法了:

  1. protected final void processRequest(HttpServletRequest request, HttpServletResponse response) 
  2.   throws ServletException, IOException { 
  3.  long startTime = System.currentTimeMillis(); 
  4.  Throwable failureCause = null
  5.  LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); 
  6.  LocaleContext localeContext = buildLocaleContext(request); 
  7.  RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); 
  8.  ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); 
  9.  WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); 
  10.  asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor()); 
  11.  initContextHolders(request, localeContext, requestAttributes); 
  12.  try { 
  13.   doService(request, response); 
  14.  } 
  15.  catch (ServletException | IOException ex) { 
  16.   failureCause = ex; 
  17.   throw ex; 
  18.  } 
  19.  catch (Throwable ex) { 
  20.   failureCause = ex; 
  21.   throw new NestedServletException("Request processing failed", ex); 
  22.  } 
  23.  finally { 
  24.   resetContextHolders(request, previousLocaleContext, previousAttributes); 
  25.   if (requestAttributes != null) { 
  26.    requestAttributes.requestCompleted(); 
  27.   } 
  28.   logResult(request, response, failureCause, asyncManager); 
  29.   publishRequestHandledEvent(request, response, startTime, failureCause); 
  30.  } 

這個(gè)方法雖然比較長(zhǎng),但是其實(shí)它的核心就是最中間的 doService 方法,以 doService 為界,我們可以將該方法的內(nèi)容分為三部分:

  1. doService 之前主要是一些準(zhǔn)備工作,準(zhǔn)備工作主要干了兩件事,第一件事就是從 LocaleContextHolder 和 RequestContextHolder 中分別獲取它們?cè)瓉?lái)保存的 LocaleContext 和 RequestAttributes 對(duì)象存起來(lái),然后分別調(diào)用 buildLocaleContext 和 buildRequestAttributes 方法獲取到當(dāng)前請(qǐng)求的 LocaleContext 和 RequestAttributes 對(duì)象,再通過(guò) initContextHolders 方法將當(dāng)前請(qǐng)求的 LocaleContext 和 RequestAttributes 對(duì)象分別設(shè)置到 LocaleContextHolder 和 RequestContextHolder 對(duì)象中;第二件事則是獲取到異步管理器并設(shè)置攔截器。
  2. 接下來(lái)就是 doService 方法,這是一個(gè)抽象方法,具體的實(shí)現(xiàn)在 DispatcherServlet 中,這個(gè)松哥放到 DispatcherServlet 中再和大家分析。
  3. 第三部分就是 finally 中,這個(gè)里邊干了兩件事:第一件事就是將 LocaleContextHolder 和 RequestContextHolder 中對(duì)應(yīng)的對(duì)象恢復(fù)成原來(lái)的樣子(參考第一步);第二件事就是通過(guò) publishRequestHandledEvent 方法發(fā)布一個(gè) ServletRequestHandledEvent 類(lèi)型的消息。

經(jīng)過(guò)上面的分析,大家發(fā)現(xiàn),processRequest 其實(shí)主要做了兩件事,第一件事就是對(duì) LocaleContext 和 RequestAttributes 的處理,第二件事就是發(fā)布事件。我們對(duì)這兩件事分別來(lái)研究。

2.1 LocaleContext 和 RequestAttributes

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

2.1.1 LocaleContext

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

國(guó)際化的時(shí)候,如果我們需要用到 Locale 對(duì)象,第一反應(yīng)就是從 HttpServletRequest 中獲取,像下面這樣:

  1. Locale locale = req.getLocale(); 

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

所以 SpringMVC 中還給我們提供了 LocaleContextHolder,這個(gè)工具就是用來(lái)保存當(dāng)前請(qǐng)求的 LocaleContext 的。當(dāng)大家看到 LocaleContextHolder 時(shí)不知道有沒(méi)有覺(jué)得眼熟,松哥在之前的 Spring Security 系列教程中和大家聊過(guò) SecurityContextHolder,這兩個(gè)的原理基本一致,都是基于 ThreadLocal 來(lái)保存變量,進(jìn)而確保不同線程之間互不干擾,對(duì) ThreadLocal 不熟悉的小伙伴,可以看看松哥的 Spring Security 系列,之前有詳細(xì)分析過(guò)(公號(hào)后臺(tái)回復(fù) ss)。

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

  1. Locale locale = LocaleContextHolder.getLocale(); 

上面這個(gè) Locale 對(duì)象實(shí)際上就是從 LocaleContextHolder 中的 LocaleContext 里邊取出來(lái)的。

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

2.1.2 RequestAttributes

RequestAttributes 是一個(gè)接口,這個(gè)接口可以用來(lái) get/set/remove 某一個(gè)屬性。

RequestAttributes 有諸多實(shí)現(xiàn)類(lèi),默認(rèn)使用的是 ServletRequestAttributes,通過(guò) ServletRequestAttributes,我們可以 getRequest、getResponse 以及 getSession。

在 ServletRequestAttributes 的具體實(shí)現(xiàn)中,會(huì)通過(guò) scope 參數(shù)判斷操作 request 還是操作 session(如果小伙伴們不記得 Spring 中的作用域問(wèn)題,可以公號(hào)后臺(tái)回復(fù) spring,看看松哥錄制的免費(fèi)的 Spring 入門(mén)教程,里邊有講),我們來(lái)看一下 ServletRequestAttributes#setAttribute 方法(get/remove 方法執(zhí)行邏輯類(lèi)似):

  1. public void setAttribute(String name, Object value, int scope) { 
  2.     if (scope == 0) { 
  3.         if (!this.isRequestActive()) { 
  4.             throw new IllegalStateException("Cannot set request attribute - request is not active anymore!"); 
  5.         } 
  6.         this.request.setAttribute(name, value); 
  7.     } else { 
  8.         HttpSession session = this.obtainSession(); 
  9.         this.sessionAttributesToUpdate.remove(name); 
  10.         session.setAttribute(name, value); 
  11.     } 

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

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

看了上面的講解,大家應(yīng)該發(fā)現(xiàn)了,在 SpringMVC 中,如果我們需要在 Controller 之外的其他地方使用 request、response 以及 session,其實(shí)不用每次都從 Controller 中傳遞 request、response 以及 session 等對(duì)象,我們完全可以直接通過(guò) RequestContextHolder 來(lái)獲取,像下面這樣:

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

是不是非常 easy!

2.2 事件發(fā)布

最后就是 processRequest 方法中的事件發(fā)布了。

在 finally 代碼塊中會(huì)調(diào)用 publishRequestHandledEvent 方法發(fā)送一個(gè) ServletRequestHandledEvent 類(lèi)型的事件,具體發(fā)送代碼如下:

  1. private void publishRequestHandledEvent(HttpServletRequest request, HttpServletResponse response, 
  2.   long startTime, @Nullable Throwable failureCause) { 
  3.  if (this.publishEvents && this.webApplicationContext != null) { 
  4.   // Whether or not we succeeded, publish an event. 
  5.   long processingTime = System.currentTimeMillis() - startTime; 
  6.   this.webApplicationContext.publishEvent( 
  7.     new ServletRequestHandledEvent(this, 
  8.       request.getRequestURI(), request.getRemoteAddr(), 
  9.       request.getMethod(), getServletConfig().getServletName(), 
  10.       WebUtils.getSessionId(request), getUsernameForRequest(request), 
  11.       processingTime, failureCause, response.getStatus())); 
  12.  } 

可以看到,事件的發(fā)送需要 publishEvents 為 true,而該變量默認(rèn)就是 true。如果需要修改該變量的值,可以在 web.xml 中配置 DispatcherServlet 時(shí),通過(guò) init-param 節(jié)點(diǎn)順便配置一下該變量的值。正常情況下,這個(gè)事件總是會(huì)被發(fā)送出去,如果項(xiàng)目有需要,我們可以監(jiān)聽(tīng)該事件,如下:

  1. @Component 
  2. public class ServletRequestHandleListener implements ApplicationListener<ServletRequestHandledEvent> { 
  3.     @Override 
  4.     public void onApplicationEvent(ServletRequestHandledEvent servletRequestHandledEvent) { 
  5.         System.out.println("請(qǐng)求執(zhí)行完畢-"+servletRequestHandledEvent.getRequestUrl()); 
  6.     } 

當(dāng)一個(gè)請(qǐng)求執(zhí)行完畢時(shí),該事件就會(huì)被觸發(fā)。

3.小結(jié)

這篇文章主要和小伙伴們分享了 SpringMVC 中 DispatcherServlet 的父類(lèi) FrameworkServlet,F(xiàn)rameworkServlet 的功能其實(shí)比較簡(jiǎn)單,主要就是在 service 方法中增加了對(duì) PATCH 的處理,然后其他類(lèi)型的請(qǐng)求都被歸類(lèi)到 processRequest 方法中進(jìn)行統(tǒng)一處理,processRequest 方法則又分了三部分,首先是對(duì) LocaleContext 和 RequestAttributes 的處理,然后執(zhí)行 doService,最后在 finally 代碼塊中對(duì) LocaleContext 和 RequestAttributes 屬性進(jìn)行復(fù)原,同時(shí)發(fā)布一個(gè)請(qǐng)求結(jié)束的事件。

本文轉(zhuǎn)載自微信公眾號(hào)「江南一點(diǎn)雨」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系江南一點(diǎn)雨公眾號(hào)。

 

責(zé)任編輯:武曉燕 來(lái)源: 江南一點(diǎn)雨
相關(guān)推薦

2011-05-26 10:05:48

MongoDB

2012-09-20 10:07:29

Nginx源碼分析Web服務(wù)器

2023-02-26 08:42:10

源碼demouseEffect

2021-07-06 09:29:38

Cobar源碼AST

2024-06-13 07:55:19

2011-05-26 16:18:51

Mongodb

2021-03-26 11:00:50

SpringMVC組件接口

2020-07-28 08:54:39

內(nèi)核通信Netlink

2012-09-06 10:07:26

jQuery

2021-09-05 07:35:58

lifecycleAndroid組件原理

2017-01-12 14:52:03

JVMFinalRefere源碼

2009-07-08 13:22:30

JDK源碼分析Set

2022-01-06 07:06:52

KubernetesResourceAPI

2022-08-27 08:02:09

SQL函數(shù)語(yǔ)法

2022-05-30 07:36:54

vmstoragevmselect

2023-03-17 07:53:20

K8sAPIServerKubernetes

2019-09-09 06:30:06

Springboot程序員開(kāi)發(fā)

2022-07-06 10:37:45

SpringServlet初始化

2019-09-20 08:54:38

KafkaBroker消息

2020-05-26 18:50:46

JVMAttachJava
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 激情自拍偷拍 | 特黄小视频 | 欧美精品一区二区三区在线播放 | 日韩 国产 在线 | 玖玖在线精品 | 欧美日韩亚洲一区 | 中文字幕在线电影观看 | 国产欧美精品在线 | 色婷婷九月 | av黄色片 | 亚洲狠狠爱 | 亚洲精品99999 | 一区二区三区av | 黄网站色大毛片 | 国产视频久久 | 中文字幕精品一区二区三区在线 | 国产三区四区 | www在线视频 | 成人在线电影在线观看 | 女朋友的闺蜜3韩国三级 | 精品久久九| 欧美精品欧美精品系列 | 黄网免费看 | 久久精品国产亚洲 | 天天看夜夜 | 久久久久国产精品www | 国产成人精品视频 | 亚洲一区免费 | 东方伊人免费在线观看 | 成人免费视频观看 | 欧美中国少妇xxx性高请视频 | 亚洲一区二区三区免费在线观看 | 欧美日韩综合一区 | 成人国产在线视频 | 日韩美女一区二区三区在线观看 | 天天爽网站| 久色视频在线观看 | 九九爱这里只有精品 | 久久福利 | 香蕉一区 | 日韩国产在线观看 |