上一篇寫到Bootstrap類的啟動流程,經(jīng)過啟動腳本,經(jīng)歷了一系列的初始化,類加載,最后通過不同的命令,執(zhí)行到不同的操作,由于是啟動過程,所以從執(zhí)行l(wèi)oad()方法開始。接下來我們簡單梳理下load方法的主要流程和一些結(jié)構(gòu)分析,從而了解到其中的啟動流程,衍生出tomcat中主要的幾個組件,以及這些組件之間的大概關(guān)系。為以后每個模塊和組件的研究打下基礎(chǔ)。
我們進入到daemon.load(args)方法,可以看到,也是使用反射調(diào)用了Catalina類的load(String args[])方法:
private void load(String[] arguments)
throws Exception {
// Call the load() method
String methodName = "load";
...
method.invoke(catalinaDaemon, param);
}
接下來進入到Catalina#load(String args[])
這個方法主要是對一些特殊命令進行處理,接下來便進入到重載的load()方法:
/**
* Start a new server instance.
*/
public void load() {
long t1 = System.nanoTime();
...
...
// Create and execute our Digester
Digester digester = createStartDigester();
...
...
getServer().setCatalina(this);
getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
// Start the new server
try {
getServer().init();
} catch (LifecycleException e) {
if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
throw new java.lang.Error(e);
} else {
log.error("Catalina.start", e);
}
}
}
這里只列出部分核心代碼。
首先,使用Digester 工具預(yù)定義了一系列的conf/server.xml配置文件的解析規(guī)則,這個工具會在以后的章節(jié)中細(xì)說,這個工具主要是定義了xml轉(zhuǎn)換為java對象的規(guī)則。
為了簡潔,我將tomcat的默認(rèn)配置文件的注釋刪掉了,如圖:

通過Digester ,可以將Catalina中的server屬性創(chuàng)建為默認(rèn)的StandardServer類,并將其中的屬性依賴關(guān)系也通過xml創(chuàng)建了。
從配置文件可以看到,Server中包含了一組Listener,用來監(jiān)聽生命周期內(nèi)的事件,這里用到了觀察者模式來做事件通知操作,后續(xù)的章節(jié)中也會詳細(xì)說明。
Server中又包括了一個Service標(biāo)簽,在使用Digester 時,創(chuàng)建了默認(rèn)的StandardService類,Service中包含了兩個Connector和一個Engine,再往里面就是Realm和Host、Valve。初步就是這么個結(jié)構(gòu),后續(xù)章節(jié)會詳細(xì)解說這些組件間的關(guān)系和作用。
解析完server.xml文件后,將當(dāng)前的Catalina對象的信息傳給server屬性,讓它們互相擁有彼此的引用,然后調(diào)用server的init()方法。
StandardServer中并沒有init方法,所以我根據(jù)其繼承關(guān)系,找到了其父類LifecycleBase的init方法,代碼如下:
@Override
public final synchronized void init() throws LifecycleException {
if (!state.equals(LifecycleState.NEW)) {
invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
}
try {
setStateInternal(LifecycleState.INITIALIZING, null, false);
initInternal();
setStateInternal(LifecycleState.INITIALIZED, null, false);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
setStateInternal(LifecycleState.FAILED, null, false);
throw new LifecycleException(
sm.getString("lifecycleBase.initFail",toString()), t);
}
}
為了防止其他線程同時調(diào)用這個方法,導(dǎo)致生命周期的狀態(tài)不正常,這個方法加了關(guān)鍵字synchronized 。這個方法其實用了模板方法設(shè)計模式,留了一個initInternal抽象鉤子方法讓子類去實現(xiàn)。
setStateInternal方法設(shè)置了當(dāng)前的state為初始化對應(yīng)的狀態(tài),我們打開期內(nèi)部,會在方法最后看到如下代碼:
String lifecycleEvent = state.getLifecycleEvent();
if (lifecycleEvent != null) {
fireLifecycleEvent(lifecycleEvent, data);
}
/**
* Allow sub classes to fire {@link Lifecycle} events.
*
* @param type Event type
* @param data Data associated with event.
*/
protected void fireLifecycleEvent(String type, Object data) {
LifecycleEvent event = new LifecycleEvent(this, type, data);
for (LifecycleListener listener : lifecycleListeners) {
listener.lifecycleEvent(event);
}
}
fireLifecycleEvent方法中其實就是通知所有的監(jiān)聽者本次發(fā)生了初始化的事件,監(jiān)聽者會根據(jù)event的類型進行不同的操作,在這里,各個Listener充當(dāng)了觀察者模式中的監(jiān)聽者,Server是監(jiān)聽目標(biāo),后續(xù)我們將會看到許多類似這樣的寫法。想深入觀察者模式的請點擊這里。
接下來我們再進入StandardServer的initInternal方法,代碼如下:
/**
* Invoke a pre-startup initialization. This is used to allow connectors
* to bind to restricted ports under Unix operating environments.
*/
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
// Register global String cache
// Note although the cache is global, if there are multiple Servers
// present in the JVM (may happen when embedding) then the same cache
// will be registered under multiple names
onameStringCache = register(new StringCache(), "type=StringCache");
// Register the MBeanFactory
MBeanFactory factory = new MBeanFactory();
factory.setContainer(this);
onameMBeanFactory = register(factory, "type=MBeanFactory");
// Register the naming resources
globalNamingResources.init();
// Populate the extension validator with JARs from common and shared
// class loaders
if (getCatalina() != null) {
ClassLoader cl = getCatalina().getParentClassLoader();
// Walk the class loader hierarchy. Stop at the system class loader.
// This will add the shared (if present) and common class loaders
while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
if (cl instanceof URLClassLoader) {
URL[] urls = ((URLClassLoader) cl).getURLs();
for (URL url : urls) {
if (url.getProtocol().equals("file")) {
try {
File f = new File (url.toURI());
if (f.isFile() &&
f.getName().endsWith(".jar")) {
ExtensionValidator.addSystemResource(f);
}
} catch (URISyntaxException e) {
// Ignore
} catch (IOException e) {
// Ignore
}
}
}
}
cl = cl.getParent();
}
}
// Initialize our defined Services
for (int i = 0; i < services.length; i++) {
services[i].init();
}
}
這里前面一大段是JMX中的注冊MBean等操作,最后循環(huán)調(diào)用了它所有service的init方法。
同理進入到init方法,還是跟Server一樣,又跳轉(zhuǎn)到了LifecycleBase的init方法,同樣地,也是進入到StandardService中的initInternal方法,代碼如下:
/**
* Invoke a pre-startup initialization. This is used to allow connectors
* to bind to restricted ports under Unix operating environments.
*/
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
if (engine != null) {
engine.init();
}
// Initialize any Executors
for (Executor executor : findExecutors()) {
if (executor instanceof JmxEnabled) {
((JmxEnabled) executor).setDomain(getDomain());
}
executor.init();
}
// Initialize mapper listener
mapperListener.init();
// Initialize our defined Connectors
synchronized (connectorsLock) {
for (Connector connector : connectors) {
try {
connector.init();
} catch (Exception e) {
String message = sm.getString(
"standardService.connector.initFailed", connector);
log.error(message, e);
if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
throw new LifecycleException(message);
}
}
}
}
首先調(diào)用了engine的init方法,在Digester 解析過程中配置了默認(rèn)的StandardEngine,老規(guī)矩,從繼承關(guān)系就可以肯定又是調(diào)用到了StandardEngine的initInternal這個鉤子方法
@Override
protected void initInternal() throws LifecycleException {
// Ensure that a Realm is present before any attempt is made to start
// one. This will create the default NullRealm if necessary.
getRealm();
super.initInternal();
}
這里是為了避免realm對象為空,如果為null,會新建一個NullRealm對象來初始化。
然后調(diào)用了父類ContainerBase的initInternal方法,初始化了啟動和停止的線程池startStopExecutor。
再回到StandardService中的initInternal方法,這里初始化了executors,等,最后初始化了connectors,也是默認(rèn)的Connector類。
看到這里,我們結(jié)合server.xml,對于tomcat的初始化流程和一些組件的關(guān)系,應(yīng)該有了一個初步的了解了