Dev Study
← 解説「Maven は何を変えたか」に戻る

サンプルコードで身につける: Maven は何を変えたか

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

1最小の pom.xml を読む

2と依存1本のアプリをビルドできる、最小の の全文です(20行)。ソースの場所も出力先も、コンパイルして にする手順も書かれていません。依存は、強調した ・artifactId・version の3行(座標)だけで指定します。

pom.xml(Maven 3.9.16 でビルドできた最小形。全文)設定ファイル
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
 
<groupId>com.example</groupId>
<artifactId>hello</artifactId>
<version>1.0.0</version>
 
<properties>
<maven.compiler.release>25</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
 
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.21.0</version>
</dependency>
</dependencies>
</project>

2mvn package がやったことと、出来たもの

上の pom.xml で を実行したログの骨格と、ビルド後の です。 に書いていないのに、 のソースが へコンパイルされ、target/hello-1.0.0.jar が出来ています。JAR の名前は artifactId と version から決まります。

$ mvn -B -ntp package(1回目。空行とダウンロードの行を除く)と、ビルド後の treeコマンド出力
$ mvn -B -ntp package
[INFO] Building hello 1.0.0
[INFO] --- resources:3.4.0:resources (default-resources) @ hello ---
[INFO] skip non existing resourceDirectory /work/f-maven/std/src/main/resources
[INFO] --- compiler:3.15.0:compile (default-compile) @ hello ---
[INFO] Compiling 2 source files with javac [debug release 25] to target/classes
[INFO] --- resources:3.4.0:testResources (default-testResources) @ hello ---
[INFO] --- compiler:3.15.0:testCompile (default-testCompile) @ hello ---
[INFO] No sources to compile
[INFO] --- surefire:3.5.4:test (default-test) @ hello ---
[INFO] No tests to run.
[INFO] --- jar:3.5.0:jar (default-jar) @ hello ---
[INFO] Building jar: /work/f-maven/std/target/hello-1.0.0.jar
[INFO] BUILD SUCCESS
 
$ tree(ビルド後。target/ の管理用ファイルは省略)
std
├── pom.xml
├── src
│ └── main
│ └── java
│ └── com
│ └── example
│ ├── app
│ │ └── Main.java
│ └── greet
│ └── Greeter.java
├── target
│ ├── classes
│ │ └── com
│ │ └── example
│ │ ├── app
│ │ │ └── Main.class
│ │ └── greet
│ │ └── Greeter.class
│ ├── generated-sources
│ │ └── annotations
│ ├── hello-1.0.0.jar
...

3依存の実体はプロジェクトの外にある(node_modules との違い)

空の状態から上のプロジェクトを1回ビルドした後の、()です。アプリの依存は1本ですが、 自身のとその依存も同じ場所に入り、JAR は47個になりました。 は版ごとのディレクトリに分かれて並んでいます。node_modules と違ってプロジェクトの外にあり、全プロジェクトで共用されます。

ローカルリポジトリの中(Maven 3.9.16、1回の値)コマンド出力
$ find ~/.m2/repository -name "*.jar" | wc -l (after the first, cold `mvn package` of this 2-class project)
47
$ find ~/.m2/repository -name "*.pom" | wc -l
120
$ du -sh ~/.m2/repository
22M
$ find ~/.m2/repository/org/apache/commons/commons-lang3 -type f
repository/org/apache/commons/commons-lang3/3.16.0/_remote.repositories
repository/org/apache/commons/commons-lang3/3.16.0/commons-lang3-3.16.0.pom
repository/org/apache/commons/commons-lang3/3.16.0/commons-lang3-3.16.0.pom.sha1
repository/org/apache/commons/commons-lang3/3.18.0/_remote.repositories
repository/org/apache/commons/commons-lang3/3.18.0/commons-lang3-3.18.0.jar
repository/org/apache/commons/commons-lang3/3.18.0/commons-lang3-3.18.0.jar.sha1
repository/org/apache/commons/commons-lang3/3.18.0/commons-lang3-3.18.0.pom
repository/org/apache/commons/commons-lang3/3.18.0/commons-lang3-3.18.0.pom.sha1
repository/org/apache/commons/commons-lang3/3.20.0/_remote.repositories
repository/org/apache/commons/commons-lang3/3.20.0/commons-lang3-3.20.0.jar
repository/org/apache/commons/commons-lang3/3.20.0/commons-lang3-3.20.0.jar.sha1
repository/org/apache/commons/commons-lang3/3.20.0/commons-lang3-3.20.0.pom
repository/org/apache/commons/commons-lang3/3.20.0/commons-lang3-3.20.0.pom.sha1
repository/org/apache/commons/commons-lang3/3.21.0/_remote.repositories
repository/org/apache/commons/commons-lang3/3.21.0/commons-lang3-3.21.0.jar
repository/org/apache/commons/commons-lang3/3.21.0/commons-lang3-3.21.0.jar.sha1
repository/org/apache/commons/commons-lang3/3.21.0/commons-lang3-3.21.0.pom
repository/org/apache/commons/commons-lang3/3.21.0/commons-lang3-3.21.0.pom.sha1

4最小の pom で作った JAR は java -jar で動かない

JAR の中には自分のクラス2個と pom の写ししか入っていません。に入口のクラス()が無いので は失敗します。 をローカルリポジトリから に並べ、入口のクラスを名指しすると動きます。

target/hello-1.0.0.jar の中身と実行(JDK 25)コマンド出力
$ jar tf target/hello-1.0.0.jar
META-INF/
META-INF/MANIFEST.MF
com/
com/example/
com/example/greet/
com/example/app/
META-INF/maven/
META-INF/maven/com.example/
META-INF/maven/com.example/hello/
com/example/greet/Greeter.class
com/example/app/Main.class
META-INF/maven/com.example/hello/pom.xml
META-INF/maven/com.example/hello/pom.properties
$ unzip -p target/hello-1.0.0.jar META-INF/MANIFEST.MF
Manifest-Version: 1.0
Created-By: Maven JAR Plugin 3.5.0
Java-Version: 25
Build-Jdk-Spec: 25
 
$ java -jar target/hello-1.0.0.jar
no main manifest attribute, in target/hello-1.0.0.jar
[exit 1]
$ java -cp target/hello-1.0.0.jar:$HOME/.m2/repository/org/apache/commons/commons-lang3/3.21.0/commons-lang3-3.21.0.jar com.example.app.Main
Hello, World!
[exit 0]

5Ant と Maven で書く量を比べる(条件を揃えて)

同じアプリを と Maven で書いたときの対比です。20行と42行を単純に比べると不公平で、Ant 側には Main-Class の記述と実行用のが含まれています。Maven 側で Main-Class を足すと36行になります。それでも依存は JAR に入らないので、java -jar は になりました。

build.xml と pom.xml の対比(行数は空行込み。Ant 1.10.18 / Maven 3.9.16)表
Ant (build.xml) Maven 最小 (pom.xml) Maven + Main-Class (pom.xml)
行数 42 20 36
ソースの場所 書く (src.dir) 書かない (src/main/java) 書かない
出力の場所 書く (build/classes, dist) 書かない (target/) 書かない
手順と順序 全部書く (target, depends) 書かない (決まっている) 書かない
依存 lib/ に JAR を人が置く 座標3行。Maven が取ってくる 座標3行
Main-Class manifest に1行 無し maven-jar-plugin の設定15行
実行 run ターゲット8行を自作 無し (別プラグインが要る) 無し
ビルドのコマンド ant (ターゲット名は自作) mvn package mvn package

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