Mavenの学習

📢 この記事は gemini-3-flash-preview によって翻訳されました

Mavenは、Javaプロジェクトの管理とビルドを行うためのツールだよ。

Mavenを使うと、プロジェクトが依存しているリソース(jarパッケージ)を簡単かつスピーディに管理できて、バージョンの衝突も避けられるんだ。標準的で統一されたプロジェクト構造や、プラットフォームに依存しない自動ビルド方法も提供してくれるよ。

インストール

公式サイト でダウンロードして解凍したら、ローカルリポジトリを設定しよう。conf/setting.xml の <localRepository> を特定のディレクトリに変更するんだ。

1
2
<!-- 例えばローカルリポジトリを D:\develop\maven_repo にする場合 -->
<localRepository>D:\develop\maven_repo</localRepository>

アリババクラウド(Aliyun)のプライベートミラーを設定するには、conf/setting.xml の <mirror> タグ内に子タグを追加してね。

1
2
3
4
5
6
<mirror>
    <id>alimaven</id>
    <name>aliyun maven</name>
    <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
    <mirrorOf>central</mirrorOf>
</mirror>

環境変数の設定:MAVEN_HOME をMavenの解凍ディレクトリにして、その中の bin ディレクトリを PATH 環境変数に追加しよう。


インストール完了のテスト:コマンドラインで mvn -v を入力して出力を確認してみて。

依存関係の設定

依存関係(Dependency):プロジェクトの実行に必要な jar パッケージのこと。1つのプロジェクトに複数の依存関係を導入できるよ。

設定の手順:

  1. pom.xml に <dependencies> タグを書く
  2. <dependencies> タグの中で <dependency> を使って座標を導入する
  3. 座標の groupId, artifactId, version を定義する
  4. 設定を更新して、新しく追加した座標を取り込む
1
2
3
4
5
6
7
8
<!-- 例えば logback を導入する場合 -->
<dependencies>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.3</version>
    </dependency>
</dependencies>

依存関係の座標情報は https://mvnrepository.com/ で探せるよ。

依存関係の推移

依存関係には推移性があるんだ。

  • 直接依存:今のプロジェクトで依存設定を通じて直接作られた依存関係。
  • 間接依存:依存しているリソースが他のリソースに依存している場合、今のプロジェクトも間接的にそのリソースに依存することになる。

依存関係を追加したとき、そのパッケージが他のパッケージに依存していれば、関連するパッケージも自動的に追加されるよ。


もちろん、あるパッケージのサブパッケージが必要ない場合は、依存関係を除外することもできる(依存を自ら断ち切るリソースについては、バージョンを指定する必要はないよ)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<!-- 例えばあるプロジェクトの junit を除外する場合 -->
<dependency>
    <groupId>com.yexca</groupId>
    <artifactId>maven-project</artifactId>
    <version>1.0-SNAPSHOT</version>
    <exclusions>
        <exclusion>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </exclusion>
    </exclusions>
</dependency>

依存関係のスコープ

依存している jar パッケージは、デフォルトではどこでも使えるけど、<scope>...</scope> を使ってその有効範囲を設定できるんだ。

  • メインプログラムの範囲で有効(main フォルダ内)
  • テストプログラムの範囲で有効(test フォルダ内)
  • パッケージングして実行する際に含まれるか(package コマンドの範囲内)
scopeの値メインプログラムテストプログラムパッケージング (実行)例
compile (デフォルト)YYYlog4j
test-Y-junit
providedYY-servlet-api
runtime-YYjdbc ドライバ
1
2
3
4
5
6
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.10</version>
    <scope>test</scope>
</dependency>

ライフサイクル

Maven のライフサイクルは、すべての Maven プロジェクトのビルドプロセスを抽象化して統一するためにあるんだ。

Maven には、互いに独立した 3 つのライフサイクルがあるよ:

  • clean:掃除作業
  • default:コア作業(コンパイル、テスト、パッケージング、インストール、デプロイなど)
  • site:レポート生成、サイト公開など

それぞれのライフサイクルはいくつかのフェーズ(phase)で構成されていて、フェーズには順番があるんだ。後のフェーズは前のフェーズに依存しているよ。

clean

  • pre-clean
  • clean:前回ビルドで生成されたファイル(target フォルダ)を削除する
  • post-clean

default

  • validate
  • initialize
  • generate-sources
  • process-sources
  • generate-resources
  • process-resources
  • compile:プロジェクトのソースコードをコンパイルする
  • process-classes
  • generate-test-sources
  • process-test-sources
  • generate-test-resources
  • process-test-resources
  • test-compile
  • process-test-classes
  • test:適切なユニットテストフレームワーク(junit)を使ってテストを実行する
  • prepare-package
  • package:コンパイル後のファイルを jar や war などにパッケージングする
  • verify
  • install:プロジェクトをローカルリポジトリにインストールする
  • deploy

site

  • pre-site
  • site
  • post-site
  • site-deploy

同じライフサイクルの中で後のフェーズを実行すると、それより前のフェーズもすべて実行されるよ。

マルチモジュール設計

マルチモジュール設計とは、プロジェクトの設計段階で大きなプロジェクトをいくつかのモジュールに分割すること。各モジュールは独立していて、別のモジュールの機能が必要になったら、そのモジュールを依存関係として追加するだけでいいんだ。

共通のエンティティクラスやツールクラスを独立したモジュールにしておけば、あるモジュールでそれを使いたい時に対応する依存関係を入れるだけで済むよ。

プロジェクトの管理・メンテナンス・拡張がしやすくなるし、モジュール間の呼び出しやリソースの共有も便利になるんだ。

継承

Lombok みたいな依存関係は、プロジェクトの全モジュールで使うかもしれないよね。各モジュールで毎回宣言するのは面倒だけど、継承を使えばこの問題を解決できるよ。

継承関係

親プロジェクトを作成して、モジュール共通の依存関係を親プロジェクトにまとめて設定するんだ。子プロジェクトが親プロジェクトを継承すれば、依存関係も継承されるから、各子プロジェクトで設定する必要がなくなるよ。

継承は 2 つのプロジェクト間の関係を表すもので、Java の継承と似ているんだ。子プロジェクトは親プロジェクトの設定情報を継承できる。特によくあるのが依存関係の継承で、これによって依存設定を簡略化し、一括管理できるようになるよ。

1
2
3
4
5
6
7
<!--親プロジェクトを指定-->
<parent>
    <groupId>...</groupId>
    <artifactId>...</artifactId>
    <version>...</version>
    <relativePath>....</relativePath>
</parent>

JavaWeb 開発なら、親プロジェクトに spring-boot-starter-parent を継承させて、他のモジュールがその親プロジェクトを継承する形にできるね。

普通、ファイル構造上では、子プロジェクトは親プロジェクトのサブディレクトリになるよ。

以下は例:

  1. 親プロジェクトのパッケージング方式を pom にする
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.5</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

<groupId>net.yexca</groupId>
<artifactId>tlias-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>

Maven のパッケージング方式:

  • jar:通常のモジュール。SpringBoot プロジェクトは基本 jar パッケージ(tomcat 内蔵で動く)。
  • war:通常の Web プログラム。外部の tomcat サーバにデプロイして動かす必要がある。
  • pom:親プロジェクトや集約プロジェクト。このモジュールにはコードを書かず、依存関係の管理だけを行う。
  1. 子プロジェクトで親プロジェクトを指定する
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<parent>
    <groupId>net.yexca</groupId>
    <artifactId>tlias-parent</artifactId>
    <version>1.0-SNAPSHOT</version>
    <!-- 親プロジェクトのpomファイルの相対パスを指定(指定しない場合はローカル/リモートリポジトリから探される) -->
    <relativePath>../tlias-parent/pom.xml</relativePath>
</parent>

<!-- <groupId>net.yexca</groupId> 
-	親プロジェクトから自動継承されるので省略可能
-->
<artifactId>tlias-utils</artifactId>
<version>1.0-SNAPSHOT</version>
  1. 親プロジェクトで共通の依存関係を設定する
1
2
3
4
5
6
7
<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.24</version>
    </dependency>
</dependencies>

バージョンロック

一部のモジュールには必要だけど、他のモジュールには必要ない依存関係がある場合、親プロジェクトで依存関係の version を管理しておくと、変更が楽になるよ。

<dependencyManagement> タグで依存関係のバージョンを管理できるんだ。

親プロジェクト:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<!--依存関係のバージョンを一括管理-->
<dependencyManagement>
    <dependencies>
        <!--JWTトークン-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
    </dependencies>
</dependencyManagement>

子プロジェクト:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<dependencies>
    <!--JWTトークン-->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <!-- <version>0.9.1</version>
		- 親プロジェクトでバージョンが指定されているので、記入不要
		-->
    </dependency>
</dependencies>

親プロジェクトの <dependencyManagement> 設定はバージョンを管理するだけで、依存関係を子プロジェクトに自動導入することはないよ。子プロジェクト側で導入して初めて使えるようになるんだけど、その時にバージョン番号を指定する必要がなくなるんだ。

一方で、<dependencies> は依存関係を子プロジェクトに強制的に継承させるよ。

プロパティ設定

カスタムプロパティを使って、親プロジェクトで依存関係のバージョンを一箇所で管理・メンテナンスすることもできるよ。

プロパティの定義:

1
2
3
<properties>
    <lombok.version>1.18.24</lombok.version>
</properties>

プロパティの引用:

1
2
3
4
5
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>${lombok.version}</version>
</dependency>

これで親プロジェクトにすべてのバージョン番号をまとめて管理できるね。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>

    <lombok.version>1.18.24</lombok.version>
    <jjwt.version>0.9.1</jjwt.version>
    <aliyun.oss.version>3.15.1</aliyun.oss.version>
    <jaxb.version>2.3.1</jaxb.version>
    <activation.version>1.1.1</activation.version>
    <jaxb.runtime.version>2.3.3</jaxb.runtime.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>${lombok.version}</version>
    </dependency>
</dependencies>

<!--依存関係のバージョンを一括管理-->
<dependencyManagement>
    <dependencies>
        <!--JWTトークン-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>${jjwt.version}</version>
        </dependency>

        <!--アリババクラウドOSS-->
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>${aliyun.oss.version}</version>
        </dependency>
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>${jaxb.version}</version>
        </dependency>
        <dependency>
            <groupId>javax.activation</groupId>
            <artifactId>activation</artifactId>
            <version>${activation.version}</version>
        </dependency>
        <!-- 2.3.3以下推奨 -->
        <dependency>
            <groupId>org.glassfish.jaxb</groupId>
            <artifactId>jaxb-runtime</artifactId>
            <version>${jaxb.runtime.version}</version>
        </dependency>
    </dependencies>
</dependencyManagement>

依存関係のバージョンを変更したいときは、親プロジェクトのカスタムプロパティの値を書き換えるだけで OK だよ。

集約

あるモジュールが別のモジュールに依存している場合、そのモジュールをパッケージングする前に、まず依存先のモジュールをローカルリポジトリに install しないといけないんだ。依存モジュールが多いと、この操作がすごく面倒だよね。

Maven の集約(Aggregation)を使えば、プロジェクトの一括ビルド(クリーン、コンパイル、テスト、パッケージ、インストールなど)が簡単にできるよ。

  • 集約: 複数のモジュールを一つの全体としてまとめ、同時にプロジェクトを構築すること。
  • 集約プロジェクト: ビジネス機能を持たない「空」のプロジェクト(pom ファイルのみを持つ。一般的に、継承関係の親プロジェクトと集約プロジェクトは同じものにするよ)。
  • 役割: プロジェクトの高速ビルド(依存関係に合わせて手動でビルドする必要がなく、集約プロジェクトでビルドすれば完了する)。

集約プロジェクトの <modules> タグで、含まれる子モジュールの名称を設定できるんだ。

1
2
3
4
5
6
7
<!--他のモジュールを集約-->
<modules>
    <!-- プロジェクトのパス -->
    <module>../tlias-pojo</module>
    <module>../tlias-utils</module>
    <module>../tlias-web-management</module>
</modules>

あとは集約プロジェクトでコンパイルやパッケージング、インストールの操作をするだけで、他のプロジェクトも自動的に同期して実行されるよ。

継承集約
役割依存設定の簡略化、依存関係の一括管理プロジェクトの高速ビルド
共通点パッケージング方式はいずれも pom で、通常一つの pom にまとめるいずれも設計用のモジュールで、実際の中身(コード)はない
相違点子モジュール側で関係を設定する。
親モジュールはどの子が自分を継承しているか知らない
集約プロジェクト側で関係を設定する。
集約プロジェクトはどのモジュールが集約されているか知っている

プライベートリポジトリ(私服)

プライベートリポジトリ(私服)は特殊なリモートリポジトリで、ローカルネットワーク内に設置されるリポジトリサービスだよ。外部の中央リポジトリを代理し、チーム内部でのリソース共有や同期の問題を解決するために使われるんだ。

プロジェクトでサードパーティの依存関係を使うとき、ローカルリポジトリになければ自動的に私服に繋いでダウンロードする。私服にもなければ、私服が中央リポジトリに繋いでダウンロードし、それを私服内とローカルリポジトリの両方に保存してくれるよ。

私服は普通、会社に1台あるものだから、自分一人で設定することはあまりないかな。

リポジトリの種類とプロジェクトバージョン

私服リポジトリの説明:

  • RELEASE:自社開発した RELEASE(リリース)バージョンのリソースを保存する。
  • SNAPSHOT:自社開発した SNAPSHOT(スナップショット)バージョンのリソースを保存する。
  • Central:中央リポジトリからダウンロードしてきた依存関係を保存する。

プロジェクトバージョンの説明:

  • RELEASE(リリース版):機能が安定し、更新が止まった、配布可能なバージョン。私服の RELEASE リポジトリに保存される。
  • SNAPSHOT(快照版):機能が不安定で、開発途中のバージョン。私服の SNAPSHOT リポジトリに保存される。

設定

私服へのアクセスのためのユーザー名とパスワードを設定しよう。Maven インストールディレクトリの conf/settings.xml の servers に設定するよ。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<servers>
<server>
    <id>maven-releases</id>
    <username>admin</username>
    <password>admin</password>
</server>
    
<server>
    <id>maven-snapshots</id>
    <username>admin</username>
    <password>admin</password>
</server>
</servers>

私服から依存関係をダウンロードするためのリポジトリグループのアドレスを設定する。ファイルは同じく settings.xml の mirrors と profiles タグの中だよ。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<mirrors>
<mirror>
    <id>maven-public</id>
    <mirrorOf>*</mirrorOf>
    <url>http://127.0.0.1:8081/repository/maven-public/</url>
</mirror>
</mirrors>

<profiles>
<profile>
    <id>allow-snapshots</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
    <repositories>
        <repository>
            <id>maven-public</id>
            <url>http://127.0.0.1:8081/repository/maven-public/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
    </repositories>
</profile>
</profiles>

pom.xml にアップロード先のアドレスを設定しよう。親プロジェクトで設定すれば OK だよ。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<distributionManagement>
    <!-- RELEASE版の公開先アドレス -->
    <repository>
        <id>maven-releases</id>
        <url>http://127.0.0.1:8081/repository/maven-releases/</url>
    </repository>

    <!-- SNAPSHOT版の公開先アドレス -->
    <snapshotRepository>
        <id>maven-snapshots</id>
        <url>http://127.0.0.1:8081/repository/maven-snapshots/</url>
    </snapshotRepository>
</distributionManagement>

設定ができたら、親プロジェクトの deploy ライフサイクルを実行すれば、プロジェクトが私服リポジトリに公開されるよ。