在上一篇《查看 Tomcat 啟動文件都干點啥---catalina.bat》中,我們詳細梳理了 catalina.bat 的執行流程,最終得出了啟動 Tomcat 的核心 Java 命令。其中最關鍵的屬性是 MAINCLASS=org.apache.catalina.startup.Bootstrap,該類位於 bootstrap.jar 中。本文將深入剖析 Bootstrap 類的結構與啟動邏輯,揭示 Tomcat 如何通過類加載器隔離機制實現容器啟動。

從類圖可見,Bootstrap 包含三個核心類加載器引用(commonLoader、catalinaLoader、sharedLoader)以及關鍵的啟動入口 main 方法。變量與方法命名直觀地反映了其職責。
腳本啟動 Tomcat 時,首先調用 Bootstrap 的 main 方法。該方法主要完成兩項核心任務:
if (daemon == null) {
Bootstrap bootstrap = new Bootstrap();
try {
bootstrap.init();
} catch (Throwable t) {
handleThrowable(t);
t.printStackTrace();
return;
}
daemon = bootstrap;
} else {
Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
}
首次啟動時,創建 Bootstrap 實例並調用 init() 完成初始化,最終將實例賦值給靜態變量 daemon。若作為服務運行(stop 命令在新線程),需確保當前線程上下文類加載器指向 catalinaLoader,避免類找不到異常。
public void init() throws Exception {
setCatalinaHome();
setCatalinaBase();
initClassLoaders();
Thread.currentThread().setContextClassLoader(catalinaLoader);
SecurityClassLoad.securityClassLoad(catalinaLoader);
Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.newInstance();
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);
catalinaDaemon = startupInstance;
}
init 方法的關鍵步驟:
protected ClassLoader commonLoader = null;
protected ClassLoader catalinaLoader = null;
protected ClassLoader sharedLoader = null;
Bootstrap 註釋說明:構建類加載器載入 Catalina 內部類(catalina.home/server 目錄下所有 JAR),並啟動容器。目的在於將 Catalina 內部類及其依賴(如 XML 解析器)與系統類路徑隔離,對應用層類不可见。
try {
commonLoader = createClassLoader("common", null);
if (commonLoader == null) {
commonLoader = this.getClass().getClassLoader();
}
catalinaLoader = createClassLoader("server", commonLoader);
sharedLoader = createClassLoader("shared", commonLoader);
} catch (Throwable t) {
handleThrowable(t);
log.error("Class loader creation threw exception", t);
System.exit(1);
}
commonLoader 為父加載器,catalinaLoader 與 sharedLoader 以 commonLoader 為父。若配置為空,則退回父加載器。
private ClassLoader createClassLoader(String name, ClassLoader parent) throws Exception {
String value = CatalinaProperties.getProperty(name + ".loader");
if ((value == null) || (value.equals(""))) return parent;
value = replace(value);
List<Repository> repositories = new ArrayList<>();
StringTokenizer tokenizer = new StringTokenizer(value, ",");
while (tokenizer.hasMoreElements()) {
String repository = tokenizer.nextToken().trim();
if (repository.length() == 0) continue;
try {
@SuppressWarnings("unused") URL url = new URL(repository);
repositories.add(new Repository(repository, RepositoryType.URL));
continue;
} catch (MalformedURLException e) { /* Ignore */ }
if (repository.endsWith("*.jar")) {
repository = repository.substring(0, repository.length() - "*.jar".length());
repositories.add(new Repository(repository, RepositoryType.GLOB));
} else if (repository.endsWith(".jar")) {
repositories.add(new Repository(repository, RepositoryType.JAR));
} else {
repositories.add(new Repository(repository, RepositoryType.DIR));
}
}
ClassLoader classLoader = ClassLoaderFactory.createClassLoader(repositories, parent);
// 註冊到 MBeanServer
MBeanServer mBeanServer = ...;
ObjectName objectName = new ObjectName("Catalina:type=ServerClassLoader,name=" + name);
mBeanServer.registerMBean(classLoader, objectName);
return classLoader;
}
該方法解析 catalina.properties 中的 loader 配置,支持 URL、目錄、單個 JAR、通配符 JAR 四種倉庫類型,最終委託 ClassLoaderFactory 創建 StandardClassLoader 並註冊到 MBeanServer。

CatalinaProperties 負責載入 catalina.properties。優先讀取 $CATALINA_BASE/conf/catalina.properties,不存在則讀取 bootstrap.jar 內置配置。Tomcat 7.0+ 默認配置如下:
common.loader=${catalina.home}/lib,${catalina.home}/lib/*.jar
server.loader=
shared.loader=
由於 server.loader 和 shared.loader 為空,根據創建邏輯,catalinaLoader 和 sharedLoader 均指向 commonLoader。這與 Tomcat 5.x 時代的配置(server.loader 指向 server/lib,shared.loader 指向 shared/lib)形成對比,現行版本實際上三大加載器合一,僅保留結構兼容性。

實際運行時,commonLoader 載入 ${catalina.home}/lib 下所有 JAR,catalinaLoader 與 sharedLoader 共享該加載器實例。
main 方法第二部分根據參數判斷執行啟動、停止、配置測試等命令:
String command = "start";
if (args.length > 0) command = args[args.length - 1];
if (command.equals("startd")) { ... }
else if (command.equals("stopd")) { ... }
else if (command.equals("start")) {
daemon.setAwait(true);
daemon.load(args);
daemon.start();
}
else if (command.equals("stop")) { daemon.stopServer(args); }
else if (command.equals("configtest")) { ... }
daemon.load/start/stop 等方法均通過反射調用 catalinaDaemon 對應方法,實現 Bootstrap 與 Catalina 核心邏輯的解耦。
本文詳細剖析了 Tomcat 7.0 中 Bootstrap 的核心機制:
理解 Bootstrap 原理有助於深入掌握 Tomcat 類加載架構、啟動流程及故障排查。