本文接下來(lái)介紹并分析servlet容器,servlet容器是用來(lái)處理請(qǐng)求servlet資源,并為web客戶端填充response對(duì)象的模塊。
servlet容器是org.apache.catalina.Container接口的實(shí)例,在tomcat中,有四種類型的容器,分別為Engine、Host 、Context和Wrapper。
Engine. 代表整個(gè)容器的servlet引擎。
Host.代表一個(gè)擁有一定數(shù)量Context的虛擬主機(jī)。
Context.代表一個(gè)Web項(xiàng)目.一個(gè)Context包含一個(gè)或多個(gè)Wrapper。
Wrapper.代表單獨(dú)的一個(gè)servlet。
這些容器構(gòu)成一個(gè)自頂向下的等級(jí)結(jié)構(gòu),高等級(jí)的容器可以具有多個(gè)直接下屬等級(jí)的容器實(shí)例(子容器),這有點(diǎn)類似于composite模式,不過(guò)還是有差別的
org.apache.catalina.Container接口聲明如下
//
添加
public
void
addChild(Container child);
//
刪除
public
void
removeChild(Container child);
//
查找
public
Container findChild(String name);
//
查找全部
public
Container[] findChildren();
上面方法均為操作子容器的相關(guān)方法
容器可以包含一些支持的組件,諸如載入器、記錄器、管理器、領(lǐng)域和資源等,我們可以通過(guò)編輯server.xml文件來(lái)決定使用哪種容器。
下面我們來(lái)分析servlet容器是怎么執(zhí)行任務(wù)的,這里就要提到servlet容器的管道模型,管道包含了該servlet容器將要調(diào)用的任務(wù),而閥則表示一個(gè)具體的執(zhí)行任務(wù);在servlet容器的管道中,有一個(gè)基礎(chǔ)閥,也可以添加任意數(shù)量的額外閥,閥的數(shù)量通常是指額外添加的閥的數(shù)量,不包括基礎(chǔ)閥
這里就好像servlet編程中的過(guò)濾器模型,管道好比過(guò)濾器鏈,而閥則是具體的過(guò)濾器;基礎(chǔ)閥總是最后一個(gè)執(zhí)行的。
這里涉及幾個(gè)相關(guān)的接口,包括Pipeline、Valve、ValveContext 和Contained
Pipeline接口聲明如下
public
interface
Pipeline {
public
Valve getBasic();
public
void
setBasic(Valve valve);
public
void
addValve(Valve valve);
public
Valve[] getValves();
public
void
invoke(Request request, Response response)
throws
IOException, ServletException;
public
void
removeValve(Valve valve);
}
Valve接口聲明如下
public
interface
Valve {
public
String getInfo();
public
void
invoke(Request request, Response response,
ValveContext context)
throws
IOException, ServletException;
}
ValveContext接口聲明
public
interface
ValveContext {
public
String getInfo();
public
void
invokeNext(Request request, Response response)
throws
IOException, ServletException;
}
Contained接口聲明
public
interface
Contained {
public
Container getContainer();
public
void
setContainer(Container container);
}
閥可以選擇是否實(shí)現(xiàn)該接口,設(shè)置閥與一個(gè)servlet容器相關(guān)連
下面我們來(lái)學(xué)習(xí)Wrapper容器,Wrapper容器表示一個(gè)獨(dú)立的servlet定義,負(fù)責(zé)管理其基礎(chǔ)servlet類的生命周期,它繼承了Container接口,另外添加了額外方法聲明。其中比較重要的方法聲明是load()方法和allocate()方法,均與載入及初始化servlet類相關(guān)(供基礎(chǔ)閥調(diào)用,基礎(chǔ)閥持有Wrapper容器實(shí)例引用)
下面來(lái)分析一個(gè)簡(jiǎn)單的Wrapper類,該類實(shí)現(xiàn)了org.apache.catalina.Wrapper接口和org.apache.catalina.Pipeline接口
public
class
SimpleWrapper
implements
Wrapper, Pipeline {
//
the servlet instance
private
Servlet instance =
null
;
private
String servletClass;
private
Loader loader;
private
SimplePipeline pipeline =
new
SimplePipeline(
this
);
protected
Container parent =
null
;
public
SimpleWrapper() {
pipeline.setBasic(
new
SimpleWrapperValve());
}
public
synchronized
void
addValve(Valve valve) {
pipeline.addValve(valve);
}
public
Servlet allocate()
throws
ServletException {
//
Load and initialize our instance if necessary
if
(instance==
null
) {
try
{
instance
=
loadServlet();
}
catch
(ServletException e) {
throw
e;
}
catch
(Throwable e) {
throw
new
ServletException("Cannot allocate a servlet instance"
, e);
}
}
return
instance;
}
private
Servlet loadServlet()
throws
ServletException {
if
(instance!=
null
)
return
instance;
Servlet servlet
=
null
;
String actualClass
=
servletClass;
if
(actualClass ==
null
) {
throw
new
ServletException("servlet class has not been specified"
);
}
Loader loader
=
getLoader();
//
Acquire an instance of the class loader to be used
if
(loader==
null
) {
throw
new
ServletException("No loader."
);
}
ClassLoader classLoader
=
loader.getClassLoader();
//
Load the specified servlet class from the appropriate class loader
Class classClass =
null
;
try
{
if
(classLoader!=
null
) {
classClass
=
classLoader.loadClass(actualClass);
}
}
catch
(ClassNotFoundException e) {
throw
new
ServletException("Servlet class not found"
);
}
//
Instantiate and initialize an instance of the servlet class itself
try
{
servlet
=
(Servlet) classClass.newInstance();
}
catch
(Throwable e) {
throw
new
ServletException("Failed to instantiate servlet"
);
}
//
Call the initialization method of this servlet
try
{
servlet.init(
null
);
}
catch
(Throwable f) {
throw
new
ServletException("Failed initialize servlet."
);
}
return
servlet;
}
public
Loader getLoader() {
if
(loader !=
null
)
return
(loader);
if
(parent !=
null
)
return
(parent.getLoader());
return
(
null
);
}
public
void
invoke(Request request, Response response)
throws
IOException, ServletException {
pipeline.invoke(request, response);
}
public
void
load()
throws
ServletException {
instance
=
loadServlet();
}
//
method implementations of Pipeline
public
Valve getBasic() {
return
pipeline.getBasic();
}
public
void
setBasic(Valve valve) {
pipeline.setBasic(valve);
}
public
Valve[] getValves() {
return
pipeline.getValves();
}
public
void
removeValve(Valve valve) {
pipeline.removeValve(valve);
}
}
上面的SimpleWrapper類由于實(shí)現(xiàn)了org.apache.catalina.Pipeline接口接口,同時(shí)與該接口相關(guān)的實(shí)現(xiàn)方法都是調(diào)用引用的成員變量SimplePipeline pipeline = new SimplePipeline(this)的對(duì)應(yīng)方法,因此我們可以理解為SimpleWrapper類為SimplePipeline的包裝類
在它的invoke()方法里面調(diào)用了成員變量的SimplePipeline pipeline = new SimplePipeline(this)的invoke()方法,這里構(gòu)造函數(shù)傳入SimpleWrapper實(shí)例本身,可以猜想是為了獲取其載入器及具體的servlet實(shí)現(xiàn)類(注:該方法為Container接口與Pipeline接口都具有的方法聲明,因此SimpleWrapper類只要一個(gè)實(shí)現(xiàn)),下面我們繼續(xù)分析SimplePipeline相關(guān)實(shí)現(xiàn)
public
class
SimplePipeline
implements
Pipeline {
public
SimplePipeline(Container container) {
setContainer(container);
}
//
The basic Valve (if any) associated with this Pipeline.
protected
Valve basic =
null
;
//
The Container with which this Pipeline is associated.
protected
Container container =
null
;
//
the array of Valves
protected
Valve valves[] =
new
Valve[0
];
public
void
setContainer(Container container) {
this
.container =
container;
}
public
Valve getBasic() {
return
basic;
}
public
void
setBasic(Valve valve) {
this
.basic =
valve;
((Contained) valve).setContainer(container);
}
public
void
addValve(Valve valve) {
if
(valve
instanceof
Contained)
((Contained) valve).setContainer(
this
.container);
synchronized
(valves) {
Valve results[]
=
new
Valve[valves.length +1
];
System.arraycopy(valves,
0, results, 0
, valves.length);
results[valves.length]
=
valve;
valves
=
results;
}
}
public
Valve[] getValves() {
return
valves;
}
public
void
invoke(Request request, Response response)
throws
IOException, ServletException {
//
Invoke the first Valve in this pipeline for this request
(
new
SimplePipelineValveContext()).invokeNext(request, response);
}
public
void
removeValve(Valve valve) {
}
//
this class is copied from org.apache.catalina.core.StandardPipeline class's
//
StandardPipelineValveContext inner class.
protected
class
SimplePipelineValveContext
implements
ValveContext {
protected
int
stage = 0
;
public
String getInfo() {
return
null
;
}
public
void
invokeNext(Request request, Response response)
throws
IOException, ServletException {
int
subscript =
stage;
stage
= stage + 1
;
//
Invoke the requested Valve for the current request thread
if
(subscript <
valves.length) {
valves[subscript].invoke(request, response,
this
);
}
else
if
((subscript == valves.length) && (basic !=
null
)) {
basic.invoke(request, response,
this
);
}
else
{
throw
new
ServletException("No valve"
);
}
}
}
//
end of inner class
}
invoke()方法里面調(diào)用內(nèi)部類SimplePipelineValveContext(實(shí)現(xiàn)了ValveContext接口),遍歷執(zhí)行各個(gè)閥的invoke()方法
wrapper容器執(zhí)行的基本流程如上所述,下面我們來(lái)進(jìn)一步分析相關(guān)輔助類及實(shí)現(xiàn)類等
在應(yīng)用初始化servlet容器時(shí),我們需要為其指定一個(gè)載入器,下面是一個(gè)簡(jiǎn)單的載入器,實(shí)現(xiàn)了Loader接口
public
class
SimpleLoader
implements
Loader {
public
static
final
String WEB_ROOT =
System.getProperty(
"user.dir") + File.separator + "webroot"
;
ClassLoader classLoader
=
null
;
Container container
=
null
;
public
SimpleLoader() {
try
{
URL[] urls
=
new
URL[1
];
URLStreamHandler streamHandler
=
null
;
File classPath
=
new
File(WEB_ROOT);
String repository
= (
new
URL("file",
null
, classPath.getCanonicalPath() +
File.separator)).toString() ;
urls[
0] =
new
URL(
null
, repository, streamHandler);
classLoader
=
new
URLClassLoader(urls);
}
catch
(IOException e) {
System.out.println(e.toString() );
}
}
public
ClassLoader getClassLoader() {
return
classLoader;
}
public
Container getContainer() {
return
container;
}
//
這里省略其余代碼
}
基礎(chǔ)閥是干嘛的呢,具體來(lái)說(shuō)是調(diào)用具體servlet的service()方法(管道持有對(duì)基礎(chǔ)閥的引用)
public
class
SimpleWrapperValve
implements
Valve, Contained {
protected
Container container;
public
void
invoke(Request request, Response response, ValveContext valveContext)
throws
IOException, ServletException {
SimpleWrapper wrapper
=
(SimpleWrapper) getContainer();
ServletRequest sreq
=
request.getRequest();
ServletResponse sres
=
response.getResponse();
Servlet servlet
=
null
;
HttpServletRequest hreq
=
null
;
if
(sreq
instanceof
HttpServletRequest)
hreq
=
(HttpServletRequest) sreq;
HttpServletResponse hres
=
null
;
if
(sres
instanceof
HttpServletResponse)
hres
=
(HttpServletResponse) sres;
//
Allocate a servlet instance to process this request
try
{
servlet
=
wrapper.allocate();
if
(hres!=
null
&& hreq!=
null
) {
servlet.service(hreq, hres);
}
else
{
servlet.service(sreq, sres);
}
}
catch
(ServletException e) {
}
}
public
String getInfo() {
return
null
;
}
public
Container getContainer() {
return
container;
}
public
void
setContainer(Container container) {
this
.container =
container;
}
}
其他額外添加的閥本人就不在具體描述了,至此SimpleWrapper容器分析完畢!
---------------------------------------------------------------------------?
本系列How Tomcat Works系本人原創(chuàng)?
轉(zhuǎn)載請(qǐng)注明出處 博客園 刺猬的溫馴?
本人郵箱: chenying998179 # 163.com ( #改為@ )
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元

