Spring Boot アプリケーションの起動メカニズムと内部構造

Spring Boot の特徴的な機能の一つは、アプリケーションを単一の実行可能 JAR/WAR としてパッケージ化し、外部 Web サーバーなしで直接起動できることです。以下はその基本的な流れと内部動作を解説します。

サンプルプロジェクトの起動手順

git clone git@github.com:hengyunabc/spring-boot-demo.git
cd spring-boot-demo
mvn package
java -jar target/demo-0.0.1-SNAPSHOT.jar

Fat JAR の構造と役割

Maven ビルド後、生成される demo-0.0.1-SNAPSHOT.jar(Fat JAR)には以下の要素が含まれます:

  • META-INF/MANIFEST.MF: エントリポイントを指定。Main-Class は org.springframework.boot.loader.JarLauncher、Start-Class はユーザー定義のメインクラス(例: com.example.AppStarter)。
  • lib/: 依存ライブラリ群(例: Spring Beans, Tomcat Embed)。
  • org/springframework/boot/loader/: Spring Boot Loader のクラス群(JarLauncher, LaunchedURLClassLoader など)。
  • com/example/: アプリケーション本体のコンパイル済みクラス。

起動フローの核心:JarLauncher

エントリポイントである JarLauncher は次のように動作します:

  1. 自身が含まれる JAR ファイルのパスを特定し、Archive オブジェクトを生成。
  2. getNestedArchives()lib/ 配下の依存 JAR を列挙。
  3. それらの URL を基に LaunchedURLClassLoader を構築。
  4. MANIFEST.MF から Start-Class を読み込み、新スレッドで main() を実行。
protected void launchApp(String[] args, String appClass, ClassLoader loader) {
    Runnable task = new AppRunner(appClass, args, loader);
    Thread thread = new Thread(task);
    thread.setContextClassLoader(loader);
    thread.start();
}

階層的 JAR の読み込み機構

Spring Boot は標準の jar:file: URL プロトコルを拡張し、「JAR 内の JAR」を扱えるようにしています。例えば:

jar:file:/app.jar!/lib/spring-core.jar!/META-INF/MANIFEST.MF

このため、Handler クラスを自前で登録し、複数の !/ 区切りに対応。開かれた JarFileSoftReference でキャッシュされます。

開発環境や展開ディレクトリでの起動

IDE 上では通常の main() が直接実行され、クラスパス上に依存関係が配置済みのため特別な処理は不要です。一方、展開されたディレクトリでは ExplodedArchive が使われ、Fat JAR と同様のロジックで起動します。

埋め込みTomcatの初期化

Web 環境の判定後、AnnotationConfigEmbeddedWebApplicationContext が生成されます。その後、TomcatEmbeddedServletContainerFactory が Tomcat インスタンスを構成:

Tomcat server = new Tomcat();
server.setBaseDir(createTempDir("tomcat").getAbsolutePath());
Connector connector = new Connector("HTTP/1.1");
server.getService().addConnector(connector);
// デフォルト・JSPサーブレットの追加
addDefaultServlet(server.getHost());
addJspServlet(server.getHost());

静的リソースとテンプレートの探索

  • 静的ファイル: classpath:/static/, classpath:/public/ などがデフォルト検索パス。
  • テンプレート: Thymeleaf なら classpath:/templates/、JSP なら /WEB-INF/jsp/(設定可)。

統一エラーページ

BasicErrorController/error パスでエラーを捕捉し、シンプルな HTML を返却。機密情報漏洩を防ぎます。

ログの色付け機能

ターミナル出力時に ANSI カラーを有効化。無効化するには spring.output.ansi.enabled=false を設定。

便利な内部ユーティリティ

  • PID取得: ManagementFactory.getRuntimeMXBean().getName().split("@")[0]
  • Java Agent検出: JVM 引数に -javaagent: が含まれるかチェック。
  • メインクラス推定: スタックトレースから main メソッドを持つクラスを探索。

考慮すべき制限事項

  • 実行時、相対パス API(例: getRealPath("/"))が利用不可。
  • ログやデータ保存先の標準化が未定義(慣例として ${user.home}/.appname など)。
  • 設定がコード内に集中しがちで、複雑になると可読性が低下する場合あり。

Spring Boot は「Archive」抽象化とクラスローダーの工夫により、開発者が意識することなく依存関係を包括的に管理する仕組みを実現しています。これは単なる利便性を超え、マイクロサービス時代におけるアプリケーション配布のベストプラクティスとして広く受け入れられています。

タグ: Spring Boot JarLauncher LaunchedURLClassLoader TomcatEmbedded ResourceLoading

6月30日 19:03 投稿