首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

深入解析 Tomcat 啟動核心:Bootstrap.java 原理剖析

     編輯:Java 相關知識

前言

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

Bootstrap 類結構概覽

Bootstrap 類圖

從類圖可見,Bootstrap 包含三個核心類加載器引用(commonLoader、catalinaLoader、sharedLoader)以及關鍵的啟動入口 main 方法。變量與方法命名直觀地反映了其職責。

main 方法:啟動入口與命令分發

腳本啟動 Tomcat 時,首先調用 Bootstrap 的 main 方法。該方法主要完成兩項核心任務:

  1. 初始化守護進程實例
  2. 解析命令參數並分發執行

守護進程初始化流程

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,避免類找不到異常。

init 方法:類加載器構建與 Catalina 實例化

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 方法的關鍵步驟:

  • 設置 Catalina 家目錄與基礎目錄
  • 初始化三大類加載器(common、catalina、shared)
  • 將當前線程上下文類加載器設為 catalinaLoader
  • 通過反射加載 Catalina 啟動類,實例化並注入 sharedLoader 作為父類加載器
  • 將 Catalina 實例保存至 catalinaDaemon 供後續命令調用

類加載器體系深度解析

三大類加載器定義與職責

protected ClassLoader commonLoader = null;
protected ClassLoader catalinaLoader = null;
protected ClassLoader sharedLoader = null;

Bootstrap 註釋說明:構建類加載器載入 Catalina 內部類(catalina.home/server 目錄下所有 JAR),並啟動容器。目的在於將 Catalina 內部類及其依賴(如 XML 解析器)與系統類路徑隔離,對應用層類不可见。

initClassLoaders 實現

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 為父。若配置為空,則退回父加載器。

createClassLoader 核心邏輯

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 與默認配置

CatalianProperties 類圖

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 的核心機制:

  1. 通過 commonLoader=catalinaLoader=sharedLoader 實現類加載隔離與共享
  2. 採用反射機制動態加載 Catalina 並委託命令執行
  3. 基於 catalina.properties 配置靈活定義類加載路徑

理解 Bootstrap 原理有助於深入掌握 Tomcat 類加載架構、啟動流程及故障排查。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved