Dev Study
← 解説「実行可能 JAR の中身」に戻る

サンプルコードで身につける: 実行可能 JAR の中身

解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。

1target にできる2つの JAR

のあとの には が2つあります。 のほうは自分のだけを入れた普通の JAR(13エントリ・3,269バイト)で、それを包み直して依存とローダを足したものが 19,905,070 バイトのです。

target の JAR 2つと、包み直す前の JAR の中身(Boot 4.1.1 / Maven 3.9.16。$ の行は再構成)コマンド出力
$ cd target && ls -l *.jar *.jar.original
-rw-r--r-- 1 root root 19905070 Oct 1 13:27 demo-0.0.1-SNAPSHOT.jar
-rw-r--r-- 1 root root 3269 Oct 1 13:27 demo-0.0.1-SNAPSHOT.jar.original
 
$ unzip -l demo-0.0.1-SNAPSHOT.jar.original
Archive: demo-0.0.1-SNAPSHOT.jar.original
Length Date Time Name
--------- ---------- ----- ----
0 2026-10-01 13:27 META-INF/
167 2026-10-01 13:27 META-INF/MANIFEST.MF
0 2026-10-01 13:27 com/
0 2026-10-01 13:27 com/example/
0 2026-10-01 13:27 com/example/demo/
0 2026-10-01 13:27 META-INF/maven/
0 2026-10-01 13:27 META-INF/maven/com.example/
0 2026-10-01 13:27 META-INF/maven/com.example/demo/
29 2026-10-01 13:27 application.properties
771 2026-10-01 13:27 com/example/demo/HelloController.class
733 2026-10-01 13:27 com/example/demo/DemoApplication.class
1479 2026-10-01 13:18 META-INF/maven/com.example/demo/pom.xml
59 2026-10-01 13:27 META-INF/maven/com.example/demo/pom.properties
--------- -------
3238 13 files

2MANIFEST.MF の Main-Class は自分のクラスではない

成果物の JAR に入っている の全文です。 は のローダである で、自分のクラスは という別の項目に書かれています。リファレンスは Launcher を「It is the actual Main-Class in your jar file, and it is used to setup an appropriate and ultimately call your () method.」と説明しています。

成果物の JAR の META-INF/MANIFEST.MF(unzip -p で取り出したもの)設定ファイル
Manifest-Version: 1.0
Created-By: Maven JAR Plugin 3.5.1
Java-Version: 25
Build-Jdk-Spec: 25
Implementation-Title: demo
Implementation-Version: 0.0.1-SNAPSHOT
Main-Class: org.springframework.boot.loader.launch.JarLauncher
Start-Class: com.example.demo.DemoApplication
Spring-Boot-Version: 4.1.1
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
Spring-Boot-Layers-Index: BOOT-INF/layers.idx

3依存 JAR は無圧縮のまま並んでいる

で大きい順に5つ見た結果です。2列目の Stored は無圧縮という意味で、入れ子の JAR 34本はすべてこの形でした。全体の圧縮率は 1% で、成果物の大半は「すでに圧縮済みの JAR をそのまま並べたもの」です(webmvc だけの場合の値で、依存を足せば増えます)。

unzip -v demo-0.0.1-SNAPSHOT.jar の出力から、BOOT-INF/lib の大きい順5行と末尾の合計行コマンド出力
3623633 Stored 3623633 0% 2026-07-03 07:01 4bd7d8d3 BOOT-INF/lib/tomcat-embed-core-11.0.24.jar
2226342 Stored 2226342 0% 1980-02-01 00:00 a59abb60 BOOT-INF/lib/spring-web-7.0.9.jar
2035755 Stored 2035755 0% 1980-02-01 00:00 17e57eb1 BOOT-INF/lib/spring-core-7.0.9.jar
1944003 Stored 1944003 0% 2026-07-08 01:30 86738493 BOOT-INF/lib/jackson-databind-3.1.5.jar
1399435 Stored 1399435 0% 1980-02-01 00:00 096d6dc8 BOOT-INF/lib/spring-context-7.0.9.jar
...
20101026 19871250 1% 170 files

4素の Java で見る: 入れ子の JAR は1つのエントリでしかない

JAR は ZIP なので、java.util.zip だけで同じ形を作れます。外側から見ると、入れ子の JAR は PK で始まるバイト列を持つ1エントリで、中のクラスにたどり着くにはそのバイト列をもう一度 ZIP として開く係が要ります。 のローダが引き受けているのはこの役目です(このコードはローダの実装ではなく、構造を見るためのものです)。

Java

5NestJS・Rust と並べる: 何を成果物に入れたか

同じ応答を返す3つのアプリで、ビルドの成果物と、実行に別途要るものを並べた実測値です。Spring Boot は依存を JAR の中に入れ、NestJS は成果物の外(node_modules)に置き、Rust は実行ファイルに結合しています。ただし Rust も libc・libm・libgcc_s には動的にリンクしています。

3者の成果物(Boot 4.1.1 / NestJS 12 系 + Node 24 / Rust 1.98.1 + axum 0.8.9。arm64 Linux)表
Spring Boot NestJS Rust(axum)
ビルドの成果物 demo-0.0.1-SNAPSHOT.jar dist/ の9ファイル target/release/demo
1個・19,905,070 B (.js 3・.d.ts 3・.map 3) 1個・1,979,160 B
計 3,916 B
依存の置き場 JAR の中 成果物の外 ソースからコンパイルして結合
BOOT-INF/lib に34本 node_modules(本番依存のみで (Compiling 51行・自分を含む)
98パッケージ・22,772 KB)
実行に別途要るもの JRE(194,344 KB) Node 本体(122,893,672 B) libc など OS の共有ライブラリ
と node_modules
起動コマンド java -jar demo.jar node dist/main.js ./demo

図やコードに出てくる用語(タップで説明)