第十七課: StandardWrapper
課前復習:
?????? 不知道大家是否還有印象,就是在 6 、 7 節課說的 4 種 container, 粗略的從大到小來說就是 engine,host,context , 和 wrapper 。當時寫的時候很糾結,因為后面有詳細介紹這 4 個的計劃,所以前面寫的可能不是很詳盡。
?????? 讓我們回憶一下,當一個請求到來的時候,發生了什么。比如什么創建 Request 這里就不說了,之后 connector 會調用與之關聯的容器的 invoke 方法,之后那就肯定會調用 pipeline 的 invoke ,之后一頓 invoke valve 。好,那讓我們回想一下之前寫過的 context 和 wrapper ,總結一個比較詳細的執行過程。
1. ?????? Connector 創建 req 和 resp
2. ?????? 調用 StandardContext 的 invoke ,調用 xxxPipeline 的 invoke 方法
3. ?????? Pipeline 調用了 wrapper 的 invoke 方法
4. ?????? Wrapper 調用 valve 的 invoke 方法
5. ?????? valve 調用了 servlet 的 allocate (這里在以前的課程中講過)
6. ?????? allocate 方法調用 servlet 的 load 方法 ( 當 servlet 需要加載的時候 )
7. ?????? init 方法,之后就是 servlet 處理了。
關于 SingleThreadModel
?????? 這個 SingleThreadModel 在 servlet2.4 以上版本就已經移除了,因為這個東西只能給你的 servlet 的 service 保證同一時刻只有一個進程在訪問,給人一種假象的安全。而且只是給 service 方法給予同步,這顯然是不能完全解決多線程訪問的問題的。其實說這個是為了給下面的 StandardWrapper 做鋪墊。因為我們都知道 StrandardWrapper 是負責加載它所代表的 servlet 并 allocate 一個對象的實例。之后交給 valve 來調用 servlet 的 service 方法。這個 allocate 在使用 和 不使用 SingleThreadModel 的時候是不同的。好的,我們接下來就說這個 StandardWrapper 。
StandardWrapper
?????? 這個之前介紹過了,我們這次主要介紹的是 allocate 方法, 我們看下面這一段源碼可以發現,當沒有實現 SingleThreadModel 的時候, allocate 總是返回第一次時候產生的 servlet 實例。而如果實現 SingleThreadModel 接口,那么就開始控制分配的數量,當分配的大于 nInstance 時候,就 load 一個 servlet 實例,當然這個 load 實在 maxInstance 控制之內的。
?????? 代碼如下。
?
public Servlet allocate() throws ServletException { if (debug >= 1) log("Allocating an instance"); // If we are currently unloading this servlet, throw an exception if (unloading) throw new ServletException (sm.getString("standardWrapper.unloading", getName())); // If not SingleThreadedModel, return the same instance every time if (!singleThreadModel) { // Load and initialize our instance if necessary if (instance == null) { synchronized (this) { if (instance == null) { try { instance = loadServlet(); } catch (ServletException e) { throw e; } catch (Throwable e) { throw new ServletException (sm.getString("standardWrapper.allocate"), e); } } } } if (!singleThreadModel) { if (debug >= 2) log(" Returning non-STM instance"); countAllocated++; return (instance); } } synchronized (instancePool) { while (countAllocated >= nInstances) { // Allocate a new instance if possible, or else wait if (nInstances < maxInstances) { try { instancePool.push(loadServlet()); nInstances++; } catch (ServletException e) { throw e; } catch (Throwable e) { throw new ServletException (sm.getString("standardWrapper.allocate"), e); } } else { try { instancePool.wait(); } catch (InterruptedException e) { ; } } } if (debug >= 2) log(" Returning allocated STM instance"); countAllocated++; return (Servlet) instancePool.pop(); } }
?
Load
?????? 這個沒什么多說的,以前說過了,我們知道 wrapper 接口有一個 load 方法,其實他和 Allocate 里的一樣,都是調用的 LoadServlet 方法。我們來看看它的源碼。
? ? 先看看這一段。
?
// 這里是說如果不是第一次訪問了,并且不是singleThreadModel //就直接返回 Servlet實例 if (!singleThreadModel && (instance != null)) return instance; if ((actualClass == null) && (jspFile != null)) { Wrapper jspWrapper = (Wrapper) ((Context) getParent()).findChild(Constants.JSP_SERVLET_NAME); if (jspWrapper != null) { actualClass = jspWrapper.getServletClass(); // Merge init parameters String paramNames[] = jspWrapper.findInitParameters(); for (int i = 0; i < paramNames.length; i++) { if (parameters.get(paramNames[i]) == null) { parameters.put (paramNames[i], jspWrapper.findInitParameter(paramNames[i])); } } } }? ? ?這是Tomcat4 的方法,以后的版本就沒有了。都是直接loadservletclass。之后就是Loader了,回憶一下我們當時講的,是自定義的一個classLoader,需要注意的是,container提供一個特殊的servlet,可以訪問container的內部內容,名稱以org.apache.catalina.起始。之后就是加載servlet,之后就是權限驗證,沒啥說的。在之后就是在init()的前后fire事件。之后用instanceof來確定是否是實現了singleThreadModel的,如果是就放入pool(如果pool為空就創建一個新的)中。剩下就是Return了。
?
ServletConfig
?????? 接下來就是 servletConfig ,這個東西我們回憶一下是在哪里看到這個東西的,想想在 servlet 被 load 的時候,就會調用一個 init 方法,而且他的參數是 ServletConfig config ,那么,我們就需要知道 servlet 是怎么拿到 servletConfig 的,我們看源碼,會發現 StandardWrapper 他實現了 ServletConfig 接口,所以說他把自己傳過去就行了。但是想一個事情,如果這樣可以,那么 servlet 的每一個實現就都可以用 wrapper 之內的方法了, wrapper 是 container 的,所以我們要保證安全,那么就像 req,resp 那樣,用一個 fa?ade 設計模式就行,這個就是隱藏子系統。
?
Filter
?????? 做過 web 開發,大家應該知道 filter 這個東西,那么 filter 是怎么實現的呢,我們想一下之前學的東西,我們應該能想到 ServletWrapperValve 做了這件事情,因為 filter 是跟 service 方法前后后關系。那么我們就可以知道 valve 的 invoke 方法做了什么:
1. ?????? 首先要得到一個 wrapper 的對象, allocate 一個 servlet
2. ?????? 來一系列的 Filter ,調用他們的 doFilter 方法,當然還有 service 方法
3. ?????? 銷毀,這個應該是在 servlet 超時的時候才進行。
剩下就是講解一下 filter 的 chain 了,這個東西其實是一個 ArrayList ,但是注意 filterChain 是一個對象,其中的 filter 是一個該對象之中的一個 arraylist, 所以我們就知道,這個東西其實就是靠 iterator 來實現一個接一個的調用過濾器。所以,本章就結束了,這章其實沒有多少自己寫的東西,基本都是對源代碼的一種分析,希望大家可以多理解,或者下一個 tomcat 的 sourcecode 版本,之后多用斷點 跑一跑就能理解是怎么工作的了。
? ? 最后還是冒充一下大神說說讀代碼的事吧,剛才有人問了問為啥我能讀懂一些,剛開始的時候我也看不懂,其實就是你最開始看的時候不要糾結于所有的語句,至少你能看懂這個方法主要是干啥功能的,大概哪幾個語句能實現就行了,等你慢慢的看完這個,再看完另一個,你就能知道到底那個語句是干什么的了。
?
?
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
