第十七課: StandardWrapper
課前復(fù)習(xí):
?????? 不知道大家是否還有印象,就是在 6 、 7 節(jié)課說的 4 種 container, 粗略的從大到小來說就是 engine,host,context , 和 wrapper 。當時寫的時候很糾結(jié),因為后面有詳細介紹這 4 個的計劃,所以前面寫的可能不是很詳盡。
?????? 讓我們回憶一下,當一個請求到來的時候,發(fā)生了什么。比如什么創(chuàng)建 Request 這里就不說了,之后 connector 會調(diào)用與之關(guān)聯(lián)的容器的 invoke 方法,之后那就肯定會調(diào)用 pipeline 的 invoke ,之后一頓 invoke valve 。好,那讓我們回想一下之前寫過的 context 和 wrapper ,總結(jié)一個比較詳細的執(zhí)行過程。
1. ?????? Connector 創(chuàng)建 req 和 resp
2. ?????? 調(diào)用 StandardContext 的 invoke ,調(diào)用 xxxPipeline 的 invoke 方法
3. ?????? Pipeline 調(diào)用了 wrapper 的 invoke 方法
4. ?????? Wrapper 調(diào)用 valve 的 invoke 方法
5. ?????? valve 調(diào)用了 servlet 的 allocate (這里在以前的課程中講過)
6. ?????? allocate 方法調(diào)用 servlet 的 load 方法 ( 當 servlet 需要加載的時候 )
7. ?????? init 方法,之后就是 servlet 處理了。
關(guān)于 SingleThreadModel
?????? 這個 SingleThreadModel 在 servlet2.4 以上版本就已經(jīng)移除了,因為這個東西只能給你的 servlet 的 service 保證同一時刻只有一個進程在訪問,給人一種假象的安全。而且只是給 service 方法給予同步,這顯然是不能完全解決多線程訪問的問題的。其實說這個是為了給下面的 StandardWrapper 做鋪墊。因為我們都知道 StrandardWrapper 是負責(zé)加載它所代表的 servlet 并 allocate 一個對象的實例。之后交給 valve 來調(diào)用 servlet 的 service 方法。這個 allocate 在使用 和 不使用 SingleThreadModel 的時候是不同的。好的,我們接下來就說這個 StandardWrapper 。
StandardWrapper
?????? 這個之前介紹過了,我們這次主要介紹的是 allocate 方法, 我們看下面這一段源碼可以發(fā)現(xiàn),當沒有實現(xiàn) SingleThreadModel 的時候, allocate 總是返回第一次時候產(chǎn)生的 servlet 實例。而如果實現(xiàn) SingleThreadModel 接口,那么就開始控制分配的數(shù)量,當分配的大于 nInstance 時候,就 load 一個 servlet 實例,當然這個 load 實在 maxInstance 控制之內(nèi)的。
?????? 代碼如下。
?
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 里的一樣,都是調(diào)用的 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的內(nèi)部內(nèi)容,名稱以org.apache.catalina.起始。之后就是加載servlet,之后就是權(quán)限驗證,沒啥說的。在之后就是在init()的前后fire事件。之后用instanceof來確定是否是實現(xiàn)了singleThreadModel的,如果是就放入pool(如果pool為空就創(chuàng)建一個新的)中。剩下就是Return了。
?
ServletConfig
?????? 接下來就是 servletConfig ,這個東西我們回憶一下是在哪里看到這個東西的,想想在 servlet 被 load 的時候,就會調(diào)用一個 init 方法,而且他的參數(shù)是 ServletConfig config ,那么,我們就需要知道 servlet 是怎么拿到 servletConfig 的,我們看源碼,會發(fā)現(xiàn) StandardWrapper 他實現(xiàn)了 ServletConfig 接口,所以說他把自己傳過去就行了。但是想一個事情,如果這樣可以,那么 servlet 的每一個實現(xiàn)就都可以用 wrapper 之內(nèi)的方法了, wrapper 是 container 的,所以我們要保證安全,那么就像 req,resp 那樣,用一個 fa?ade 設(shè)計模式就行,這個就是隱藏子系統(tǒng)。
?
Filter
?????? 做過 web 開發(fā),大家應(yīng)該知道 filter 這個東西,那么 filter 是怎么實現(xiàn)的呢,我們想一下之前學(xué)的東西,我們應(yīng)該能想到 ServletWrapperValve 做了這件事情,因為 filter 是跟 service 方法前后后關(guān)系。那么我們就可以知道 valve 的 invoke 方法做了什么:
1. ?????? 首先要得到一個 wrapper 的對象, allocate 一個 servlet
2. ?????? 來一系列的 Filter ,調(diào)用他們的 doFilter 方法,當然還有 service 方法
3. ?????? 銷毀,這個應(yīng)該是在 servlet 超時的時候才進行。
剩下就是講解一下 filter 的 chain 了,這個東西其實是一個 ArrayList ,但是注意 filterChain 是一個對象,其中的 filter 是一個該對象之中的一個 arraylist, 所以我們就知道,這個東西其實就是靠 iterator 來實現(xiàn)一個接一個的調(diào)用過濾器。所以,本章就結(jié)束了,這章其實沒有多少自己寫的東西,基本都是對源代碼的一種分析,希望大家可以多理解,或者下一個 tomcat 的 sourcecode 版本,之后多用斷點 跑一跑就能理解是怎么工作的了。
? ? 最后還是冒充一下大神說說讀代碼的事吧,剛才有人問了問為啥我能讀懂一些,剛開始的時候我也看不懂,其實就是你最開始看的時候不要糾結(jié)于所有的語句,至少你能看懂這個方法主要是干啥功能的,大概哪幾個語句能實現(xiàn)就行了,等你慢慢的看完這個,再看完另一個,你就能知道到底那個語句是干什么的了。
?
?
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

