本文重點(diǎn)關(guān)注啟動(dòng)tomcat時(shí)會(huì)用到的兩個(gè)類,分別為Catalina類和Bootstrap類,它們都位于org.apachae.catalina.startup包下;Catalina類用于啟動(dòng)或關(guān)閉Server對(duì)象,并負(fù)責(zé)解析server.xml配置文件;Bootstrap類是一個(gè)入口點(diǎn),負(fù)責(zé)創(chuàng)建Catalina實(shí)例,并調(diào)用其process()方法。
org.apachae.catalina.startup.Catalina類是啟動(dòng)類,它包含一個(gè)Digester對(duì)象,用于解析位于%CATALINE_HOME%/conf目錄的server.xml文件
Catalina類還封裝了一個(gè)Server對(duì)象,該對(duì)象持有一個(gè)Service對(duì)象(Service對(duì)象包含一個(gè)Servlet容器和一個(gè)或多個(gè)連接器)。可以使用Catalina類來啟動(dòng)/關(guān)閉Server對(duì)象
可以通過實(shí)例化Catalina類,并調(diào)用其process()方法來運(yùn)行Tomcat,但在調(diào)用該方法時(shí),需要傳入適當(dāng)?shù)膮?shù)(如start或stop -help -config -debug -nonaming等)。
/**
* The instance main program.
*
*
@param
args Command line arguments
*/
public
void
process(String args[]) {
setCatalinaHome();
setCatalinaBase();
try
{
if
(arguments(args))
execute();
}
catch
(Exception e) {
e.printStackTrace(System.out);
}
}
上面方法會(huì)進(jìn)一步調(diào)用execute()方法,execute()方法會(huì)根據(jù)傳入?yún)?shù)調(diào)用start()方法或stop()方法
start方法會(huì)創(chuàng)建一個(gè)Digester對(duì)象來解析server.xml文件(Tomcat配置文件)。在解析server.xml文件之前,start方法會(huì)調(diào)用Digester對(duì)象的push方法,傳入當(dāng)前的Catalina對(duì)象為參數(shù)。這樣,Catalina對(duì)象就成了Digester對(duì)象內(nèi)部對(duì)象棧的第一個(gè)對(duì)象。解析server.xml文件后,會(huì)將變量server指向一個(gè)Server對(duì)象(默認(rèn)是org.apache.catalina.core.StandardServer類型的對(duì)象)。然后,start方法會(huì)調(diào)用server的initialize和start方法。Catalina對(duì)象的start方法會(huì)調(diào)用Server對(duì)象的await方法,server對(duì)象會(huì)使用一個(gè)專用的線程來等待關(guān)閉命令。await方法會(huì)循環(huán)等待,知道接收到正確的關(guān)閉命令。當(dāng)await方法返回時(shí),Catalina對(duì)象的start方法會(huì)調(diào)用server對(duì)象的stop方法,從而關(guān)閉server對(duì)象和其他的組件。此外,start方法還會(huì)注冊(cè)shutdown hook,確保服務(wù)器關(guān)閉時(shí)會(huì)執(zhí)行Server對(duì)象的stop方法。
start方法的實(shí)現(xiàn)如下:
/**
* Start a new server instance.
*/
protected
void
start() {
//
Create and execute our Digester
Digester digester =
createStartDigester();
File file
=
configFile();
try
{
InputSource is
=
new
InputSource("file://" +
file.getAbsolutePath());
FileInputStream fis
=
new
FileInputStream(file);
is.setByteStream(fis);
digester.push(
this
);
digester.parse(is);
fis.close();
}
catch
(Exception e) {
System.out.println(
"Catalina.start: " +
e);
e.printStackTrace(System.out);
System.exit(
1
);
}
//
Setting additional variables
if
(!
useNaming) {
System.setProperty(
"catalina.useNaming", "false"
);
}
else
{
System.setProperty(
"catalina.useNaming", "true"
);
String value
= "org.apache.naming"
;
String oldValue
=
System.getProperty(javax.naming.Context.URL_PKG_PREFIXES);
if
(oldValue !=
null
) {
value
= value + ":" +
oldValue;
}
System.setProperty(javax.naming.Context.URL_PKG_PREFIXES, value);
value
=
System.getProperty
(javax.naming.Context.INITIAL_CONTEXT_FACTORY);
if
(value ==
null
) {
System.setProperty
(javax.naming.Context.INITIAL_CONTEXT_FACTORY,
"org.apache.naming.java.javaURLContextFactory"
);
}
}
//
If a SecurityManager is being used, set properties for
//
checkPackageAccess() and checkPackageDefinition
if
( System.getSecurityManager() !=
null
) {
String access
= Security.getProperty("package.access"
);
if
( access !=
null
&& access.length() > 0
)
access
+= ","
;
else
access
= "sun.,"
;
Security.setProperty(
"package.access"
,
access
+ "org.apache.catalina.,org.apache.jasper."
);
String definition
= Security.getProperty("package.definition"
);
if
( definition !=
null
&& definition.length() > 0
)
definition
+= ","
;
else
definition
= "sun.,"
;
Security.setProperty(
"package.definition"
,
//
FIX ME package "javax." was removed to prevent HotSpot
//
fatal internal errors
definition + "java.,org.apache.catalina.,org.apache.jasper."
);
}
//
Replace System.out and System.err with a custom PrintStream
SystemLogHandler log =
new
SystemLogHandler(System.out);
System.setOut(log);
System.setErr(log);
Thread shutdownHook
= new CatalinaShutdownHook();
//
Start the new server
if
(server
instanceof
Lifecycle) {
try
{
server.initialize();
((Lifecycle) server).start();
try
{
//
Register shutdown hook
Runtime.getRuntime().addShutdownHook(shutdownHook);
}
catch
(Throwable t) {
//
This will fail on JDK 1.2. Ignoring, as Tomcat can run
//
fine without the shutdown hook.
}
//
Wait for the server to be told to shut down
server.await();
}
catch
(LifecycleException e) {
System.out.println(
"Catalina.start: " +
e);
e.printStackTrace(System.out);
if
(e.getThrowable() !=
null
) {
System.out.println(
"----- Root Cause -----"
);
e.getThrowable().printStackTrace(System.out);
}
}
}
//
Shut down the server
if
(server
instanceof
Lifecycle) {
try
{
try
{
//
Remove the ShutdownHook first so that server.stop()
//
doesn't get invoked twice
Runtime.getRuntime().removeShutdownHook(shutdownHook);
}
catch
(Throwable t) {
//
This will fail on JDK 1.2. Ignoring, as Tomcat can run
//
fine without the shutdown hook.
}
((Lifecycle) server).stop();
}
catch
(LifecycleException e) {
System.out.println(
"Catalina.stop: " +
e);
e.printStackTrace(System.out);
if
(e.getThrowable() !=
null
) {
System.out.println(
"----- Root Cause -----"
);
e.getThrowable().printStackTrace(System.out);
}
}
}
}
其中的CatalinaShutdownHook類為關(guān)閉鉤子
/**
* Shutdown hook which will perform a clean shutdown of Catalina if needed.
*/
protected
class
CatalinaShutdownHook
extends
Thread {
public
void
run() {
if
(server !=
null
) {
try
{
((Lifecycle) server).stop();
}
catch
(LifecycleException e) {
System.out.println(
"Catalina.stop: " +
e);
e.printStackTrace(System.out);
if
(e.getThrowable() !=
null
) {
System.out.println(
"----- Root Cause -----"
);
e.getThrowable().printStackTrace(System.out);
}
}
}
}
}
Catalina對(duì)象的stop方法會(huì)關(guān)閉Server對(duì)象,其實(shí)現(xiàn)如下:
/**
* Stop an existing server instance.
*/
protected
void
stop() {
//
Create and execute our Digester
Digester digester =
createStopDigester();
File file
=
configFile();
try
{
InputSource is
=
new
InputSource("file://" +
file.getAbsolutePath());
FileInputStream fis
=
new
FileInputStream(file);
is.setByteStream(fis);
digester.push(
this
); digester.parse(is);
fis.close();
}
catch
(Exception e) {
System.out.println(
"Catalina.stop: " +
e);
e.printStackTrace(System.out);
System.exit(
1
);
}
//
Stop the existing server
try
{
Socket socket
=
new
Socket("127.0.0.1"
, server.getPort());
OutputStream stream
=
socket.getOutputStream();
String shutdown
=
server.getShutdown();
for
(
int
i = 0; i < shutdown.length(); i++
)
stream.write(shutdown.charAt(i));
stream.flush();
stream.close();
socket.close();
}
catch
(IOException e) {
System.out.println(
"Catalina.stop: " +
e);
e.printStackTrace(System.out);
System.exit(
1
);
}
}
注意,stop方法通過調(diào)用createStopDigester方法創(chuàng)建一個(gè)Digester對(duì)象,然后將Catalina對(duì)象push到Digester對(duì)象的內(nèi)部對(duì)象棧中。
createStartDigester方法創(chuàng)建了一個(gè)Digester對(duì)象,然后將規(guī)則添加到其中,解析server.xml文件。添加到Digester對(duì)象中的規(guī)則是理解tomcat配置的關(guān)鍵。
createStartDigester方法的實(shí)現(xiàn)如下:
/**
* Create and configure the Digester we will be using for startup.
*/
protected
Digester createStartDigester() {
//
Initialize the digester
Digester digester =
new
Digester();
if
(debug)
digester.setDebug(
999
);
digester.setValidating(
false
);
//
Configure the actions we will be using
digester.addObjectCreate("Server"
,
"org.apache.catalina.core.StandardServer"
,
"className"
);
digester.addSetProperties(
"Server"
);
digester.addSetNext(
"Server",
"setServer",
"org.apache.catalina.Server"
);
digester.addObjectCreate(
"Server/GlobalNamingResources"
,
"org.apache.catalina.deploy.NamingResources"
);
digester.addSetProperties(
"Server/GlobalNamingResources"
);
digester.addSetNext(
"Server/GlobalNamingResources"
,
"setGlobalNamingResources"
,
"org.apache.catalina.deploy.NamingResources"
);
digester.addObjectCreate(
"Server/Listener"
,
null
,
//
MUST be specified in the element
"className"
);
digester.addSetProperties(
"Server/Listener"
);
digester.addSetNext(
"Server/Listener"
,
"addLifecycleListener"
,
"org.apache.catalina.LifecycleListener"
);
digester.addObjectCreate(
"Server/Service"
,
"org.apache.catalina.core.StandardService"
,
"className"
);
digester.addSetProperties(
"Server/Service"
);
digester.addSetNext(
"Server/Service"
,
"addService"
,
"org.apache.catalina.Service"
);
digester.addObjectCreate(
"Server/Service/Listener"
,
null
,
//
MUST be specified in the element
"className"
);
digester.addSetProperties(
"Server/Service/Listener"
);
digester.addSetNext(
"Server/Service/Listener"
,
"addLifecycleListener"
,
"org.apache.catalina.LifecycleListener"
);
digester.addObjectCreate(
"Server/Service/Connector"
,
"org.apache.catalina.connector.http.HttpConnector"
,
"className"
);
digester.addSetProperties(
"Server/Service/Connector"
);
digester.addSetNext(
"Server/Service/Connector"
,
"addConnector"
,
"org.apache.catalina.Connector"
);
digester.addObjectCreate(
"Server/Service/Connector/Factory"
,
"org.apache.catalina.net.DefaultServerSocketFactory"
,
"className"
);
digester.addSetProperties(
"Server/Service/Connector/Factory"
);
digester.addSetNext(
"Server/Service/Connector/Factory"
,
"setFactory"
,
"org.apache.catalina.net.ServerSocketFactory"
);
digester.addObjectCreate(
"Server/Service/Connector/Listener"
,
null
,
//
MUST be specified in the element
"className"
);
digester.addSetProperties(
"Server/Service/Connector/Listener"
);
digester.addSetNext(
"Server/Service/Connector/Listener"
,
"addLifecycleListener"
,
"org.apache.catalina.LifecycleListener"
);
//
Add RuleSets for nested elements
digester.addRuleSet(
new
NamingRuleSet("Server/GlobalNamingResources/"
));
digester.addRuleSet(
new
EngineRuleSet("Server/Service/"
));
digester.addRuleSet(
new
HostRuleSet("Server/Service/Engine/"
));
digester.addRuleSet(
new
ContextRuleSet("Server/Service/Engine/Default"
));
digester.addRuleSet(
new
NamingRuleSet("Server/Service/Engine/DefaultContext/"
));
digester.addRuleSet(
new
ContextRuleSet("Server/Service/Engine/Host/Default"
));
digester.addRuleSet(
new
NamingRuleSet("Server/Service/Engine/Host/DefaultContext/"
));
digester.addRuleSet(
new
ContextRuleSet("Server/Service/Engine/Host/"
));
digester.addRuleSet(
new
NamingRuleSet("Server/Service/Engine/Host/Context/"
));
digester.addRule(
"Server/Service/Engine"
,
new
SetParentClassLoaderRule(digester,
parentClassLoader));
return
(digester);
}
這里需要注意的是?digester.addSetNext("Server", "setServer","org.apache.catalina.Server")方法,該方法將Server對(duì)象壓入到Digester對(duì)象的內(nèi)部棧中,并與棧中的下一個(gè)對(duì)象相關(guān)聯(lián);在這里,下一個(gè)對(duì)象是Catalina實(shí)例,調(diào)用其setServer()方法與Server對(duì)象相關(guān)聯(lián)。
createStopDigester方法返回一個(gè)Digester對(duì)象來關(guān)閉Server對(duì)象。createStopDigester方法實(shí)現(xiàn)如下:
/**
* Create and configure the Digester we will be using for shutdown.
*/
protected
Digester createStopDigester() {
//
Initialize the digester
Digester digester =
new
Digester();
if
(debug)
digester.setDebug(
999
);
//
Configure the rules we need for shutting down
digester.addObjectCreate("Server"
,
"org.apache.catalina.core.StandardServer"
,
"className"
);
digester.addSetProperties(
"Server"
);
digester.addSetNext(
"Server",
"setServer",
"org.apache.catalina.Server");
return
(digester);
}
與啟動(dòng)Digester對(duì)象不同,關(guān)閉Digester對(duì)象只對(duì)XML文件的根元素感興趣
?org.apache.catalina.startup.Bootstrap類提供了啟動(dòng)tomcat的切入點(diǎn)(還有一些其他的類也有此功能)。當(dāng)使用bat或sh啟動(dòng)tomcat時(shí),實(shí)際上會(huì)調(diào)用該類的main方法。在main方法中會(huì)創(chuàng)建三個(gè)loader,并實(shí)例化Catalina對(duì)象,然后調(diào)用Catalina對(duì)象的process方法。
Bootstrap類的定義如下所示:
public
final
class
Bootstrap {
//
------------------------------------------------------- Static Variables
/**
* Debugging detail level for processing the startup.
*/
private
static
int
debug = 0
;
//
----------------------------------------------------------- Main Program
/**
* The main program for the bootstrap.
*
*
@param
args Command line arguments to be processed
*/
public
static
void
main(String args[]) {
//
Set the debug flag appropriately
for
(
int
i = 0; i < args.length; i++
) {
if
("-debug"
.equals(args[i]))
debug
= 1
;
}
//
Configure catalina.base from catalina.home if not yet set
if
(System.getProperty("catalina.base") ==
null
)
System.setProperty(
"catalina.base"
, getCatalinaHome());
//
Construct the class loaders we will need
ClassLoader commonLoader =
null
;
ClassLoader catalinaLoader
=
null
;
ClassLoader sharedLoader
=
null
;
try
{
File unpacked[]
=
new
File[1
];
File packed[]
=
new
File[1
];
File packed2[]
=
new
File[2
];
ClassLoaderFactory.setDebug(debug);
unpacked[
0] =
new
File(getCatalinaHome(),
"common" + File.separator + "classes"
);
packed2[
0] =
new
File(getCatalinaHome(),
"common" + File.separator + "endorsed"
);
packed2[
1] =
new
File(getCatalinaHome(),
"common" + File.separator + "lib"
);
commonLoader
=
ClassLoaderFactory.createClassLoader(unpacked, packed2,
null
);
unpacked[
0] =
new
File(getCatalinaHome(),
"server" + File.separator + "classes"
);
packed[
0] =
new
File(getCatalinaHome(),
"server" + File.separator + "lib"
);
catalinaLoader
=
ClassLoaderFactory.createClassLoader(unpacked, packed,
commonLoader);
unpacked[
0] =
new
File(getCatalinaBase(),
"shared" + File.separator + "classes"
);
packed[
0] =
new
File(getCatalinaBase(),
"shared" + File.separator + "lib"
);
sharedLoader
=
ClassLoaderFactory.createClassLoader(unpacked, packed,
commonLoader);
}
catch
(Throwable t) {
log(
"Class loader creation threw exception"
, t);
System.exit(
1
);
}
Thread.currentThread().setContextClassLoader(catalinaLoader);
//
Load our startup class and call its process() method
try
{
SecurityClassLoad.securityClassLoad(catalinaLoader);
//
Instantiate a startup class instance
if
(debug >= 1
)
log(
"Loading startup class"
);
Class startupClass
=
catalinaLoader.loadClass
(
"org.apache.catalina.startup.Catalina"
);
Object startupInstance
=
startupClass.newInstance();
//
Set the shared extensions class loader
if
(debug >= 1
)
log(
"Setting startup class properties"
);
String methodName
= "setParentClassLoader"
;
Class paramTypes[]
=
new
Class[1
];
paramTypes[
0] = Class.forName("java.lang.ClassLoader"
);
Object paramValues[]
=
new
Object[1
];
paramValues[
0] =
sharedLoader;
Method method
=
startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues);
//
Call the process() method
if
(debug >= 1
)
log(
"Calling startup class process() method"
);
methodName
= "process"
;
paramTypes
=
new
Class[1
];
paramTypes[
0] =
args.getClass();
paramValues
=
new
Object[1
];
paramValues[
0] =
args;
method
=
startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues);
}
catch
(Exception e) {
System.out.println(
"Exception during startup processing"
);
e.printStackTrace(System.out);
System.exit(
2
);
}
}
/**
* Get the value of the catalina.home environment variable.
*/
private
static
String getCatalinaHome() {
return
System.getProperty("catalina.home"
,
System.getProperty(
"user.dir"
));
}
/**
* Get the value of the catalina.base environment variable.
*/
private
static
String getCatalinaBase() {
return
System.getProperty("catalina.base"
, getCatalinaHome());
}
/**
* Log a debugging detail message.
*
*
@param
message The message to be logged
*/
private
static
void
log(String message) {
System.out.print(
"Bootstrap: "
);
System.out.println(message);
}
/**
* Log a debugging detail message with an exception.
*
*
@param
message The message to be logged
*
@param
exception The exception to be logged
*/
private
static
void
log(String message, Throwable exception) {
log(message);
exception.printStackTrace(System.out);
}
}
Bootstrap類定義了四個(gè)靜態(tài)方法,其中g(shù)etCatalinaHome返回catalina.home屬性的值,若沒有,則返回user.dir屬性的值。getCatalinaBase方法與getCatalinaHome方法類似。
Bootstrap類的main方法構(gòu)造了三個(gè)loader,之所以如此做是為了防止運(yùn)行WEB-INF/classes和WEB-INF/lib目錄外的類。
其中commonLoader允許從%CATALINA_HOME%/common/classes,%CATALINA_HOME%/common/endorsed和%CATALINA_HOME%/common/lib目錄下載入類。
catalinaLoader負(fù)責(zé)載入servlet容器需要使用的類,它只會(huì)從%CATALINA_HOME%/server/classes和%CATALINA_HOME%/server/lib目錄下查找。
sharedLoader會(huì)從%CATALINA_HOME%/shared/classes和%CATALJNA_HOME%/shared/lib目錄,以及對(duì)commonLoader可用的目錄下查找需要的類。
然后,將sharedLoader設(shè)置為每個(gè)web應(yīng)用的類載入器的父類載入器。(注意,sharedLoader并不訪問catalina的內(nèi)部類,或CLASSPATH中的類)
在創(chuàng)建了三個(gè)loader之后,main方法會(huì)載入Catalina類,實(shí)例化,并將之賦值給startupInstance變量。然后調(diào)用setParentClassLoader方法。
最后,main方法調(diào)用Catalina對(duì)象的process對(duì)象。
---------------------------------------------------------------------------?
本系列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)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元

