Mavenのマルチモジュール例

## はじめに
mavenのビルドツールを使用しマルチモジュールで動作する場合の例を作成します。
mavenのビルドツールの理解において、とりあえず動いたものからpom.xmlについて理解、mavenのビルド等を理解していくという習うより慣れろです。

## 実施内容
**WSL 上で動作確認できるシンプルな Maven マルチモジュールプロジェクト**のサンプルです。`my-app-parent` という親プロジェクトの下に `my-app-model`（モデル）と `my-app-web`（Webアプリ）という構成になります。Spring Boot を使って、`http://localhost:8080/hello` にアクセスできる簡単なWebアプリを動かします。


##  ディレクトリ構成

```bash
my-app-parent/
├── pom.xml
├── my-app-model/
│   └── pom.xml
│   └── src/main/java/com/example/model/Greeting.java
├── my-app-web/
    └── pom.xml
    └── src/main/java/com/example/web/WebApplication.java
    └── src/main/java/com/example/web/HelloController.java
```




## Spring Bootセット手順

### 1. Spring Initializr でプロジェクト雛形をダウンロード

🔗 https://start.spring.io/

設定は次のようにしてください：

| 項目             | 値                         |
|------------------|----------------------------|
| Project          | Maven                      |
| Language         | Java                       |
| Spring Boot      | 任意（例: 3.2.3）          |
| Project Metadata | group: `com.example`       |
|                  | artifact: `my-app-parent`  |
| Packaging        | Jar                        |
| Java             | 17                         |
| Dependencies     | Web（spring-boot-starter-web）|

`Generate` ボタンを押すと zip ファイルがダウンロードされます。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/ee4aec65-9c4f-436c-b442-bff59609fab9.png)


### 2. ダウンロード＆展開（WSL内）

```bash
cd ~/Desktop/work  # 作業ディレクトリに移動（例）
unzip /mnt/c/Users/apple/Downloads/my-app-parent.zip
cd my-app-parent
```


## Windows 側の ZIP ファイルを WSL から使う

WSL から Windows ファイルを見るには `/mnt/c/` を使います。
###  ダウンロード場所を確認
通常、ブラウザでダウンロードした ZIP は以下にあります：
```
C:\Users\apple\Downloads\my-app-parent.zip
```

WSL では次のように見えます：
```
/mnt/c/Users/apple/Downloads/my-app-parent.zip
```

## マルチモジュール構成へ変換

###  ① `my-app-model` フォルダを追加

```bash
mkdir my-app-model
mkdir -p my-app-model/src/main/java/com/example/model
```

#### `my-app-model/pom.xml`

```xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>com.example</groupId>
    <artifactId>my-app-parent</artifactId>
    <version>0.0.1-SNAPSHOT</version>
  </parent>

  <artifactId>my-app-model</artifactId>
  <packaging>jar</packaging>
</project>

```

#### `my-app-model/src/main/java/com/example/model/Greeting.java`

```java
package com.example.model;

public class Greeting {
    private String message;

    public Greeting(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}
```

---

###  ② `my-app-web` フォルダを追加

```bash
mkdir my-app-web
mkdir -p my-app-web/src/main/java/com/example/web
```

#### `my-app-web/pom.xml`

```xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>com.example</groupId>
    <artifactId>my-app-parent</artifactId>
    <version>0.0.1-SNAPSHOT</version>
  </parent>

  <artifactId>my-app-web</artifactId>
  <packaging>jar</packaging>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>com.example</groupId>
      <artifactId>my-app-model</artifactId>
      <version>0.0.1-SNAPSHOT</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
</project>

```

#### `my-app-web/src/main/java/com/example/web/WebApplication.java`

```java
package com.example.web;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class WebApplication {
    public static void main(String[] args) {
        SpringApplication.run(WebApplication.class, args);
    }
}
```

#### `my-app-web/src/main/java/com/example/web/HelloController.java`

```java
package com.example.web;

import com.example.model.Greeting;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    @GetMapping("/hello")
    public Greeting hello() {
        return new Greeting("こんにちは、Spring Boot + マルチモジュール from WSL!");
    }
}
```

---

### ③ 親プロジェクトの `pom.xml` を修正

現在は Spring Boot の "親" になっていますが、**マルチモジュールの "親" に変更**しましょう。

#### 置き換え後の `my-app-parent/pom.xml`

```xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.example</groupId>
  <artifactId>my-app-parent</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>pom</packaging>

  <modules>
    <module>my-app-model</module>
    <module>my-app-web</module>
  </modules>

  <properties>
    <java.version>17</java.version>
    <spring.boot.version>3.4.4</spring.boot.version>
  </properties>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>${spring.boot.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <build>
    <pluginManagement>
      <plugins>
        <plugin>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>
```

---

## ✅ ビルドと起動確認

```bash
# 親プロジェクトのルートで実行
cd ~/Desktop/work/my-app-parent

# 依存関係ビルド
mvn clean install

# Webアプリ起動（my-app-web内で）
cd my-app-web
mvn spring-boot:run
```

---

## 🌐確認

ブラウザでアクセス：

```
http://localhost:8080/hello
```

👇 JSONが出れば成功！

```json
{"message":"こんにちは、Spring Boot + マルチモジュール from WSL!"}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/7f74ece5-9edb-4c9a-82e6-c12f3404fea9.png)


## pom.xml


###  `pom.xml` の主な構成要素と役割

以下のような感じで分類できます：

### 1. `<parent>`：親プロジェクトの定義
マルチモジュールプロジェクトで共通設定をまとめたいときに使います。

```xml
<parent>
  <groupId>com.example</groupId>
  <artifactId>my-parent-project</artifactId>
  <version>1.0.0</version>
</parent>
```

🔹 役割：
- バージョンや依存ライブラリの共通化
- プラグインやプロパティの共通設定
- 子プロジェクトでの `<build>` や `<dependencies>` の継承

---

### 2. `<dependencies>`：使用するライブラリの定義

```xml
<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.7.0</version>
  </dependency>
</dependencies>
```

🔹 役割：
- プログラムで使う外部ライブラリを指定
- Maven が自動でダウンロードしてクラスパスに追加

---

### 3. `<build>`：ビルドに関する設定

```xml
<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>3.8.1</version>
      <configuration>
        <source>1.8</source>
        <target>1.8</target>
      </configuration>
    </plugin>
  </plugins>
</build>
```

🔹 役割：
- コンパイルやパッケージ化の設定
- WAR/JAR のビルド手順
- コンパイラのバージョン指定や JAR 名の指定など

---

### 4. `<properties>`：変数の定義

```xml
<properties>
  <java.version>1.8</java.version>
  <spring.boot.version>2.7.0</spring.boot.version>
</properties>
```

🔹 役割：
- バージョンや環境設定の共通化・再利用
- `<dependency>` や `<plugin>` の中で `${}` を使って参照可能



## 🍝 他言語との違い

| 言語 / フレームワーク | 依存管理ファイル         | 普段どれだけ意識するか         |
|----------------------|--------------------------|-------------------------------|
| PHP (Laravel)        | `composer.json`          | ほぼ意識せず自動でOK（artisan がやってくれる） |
| JavaScript (React)   | `package.json`           | 開発者が必要最低限だけ記述    |
| Java (Spring Boot)   | `pom.xml`                | ビルドもテストも全部ここが基盤 |
| Python (Django等)     | `requirements.txt` or `pyproject.toml` | ライブラリ管理用で軽量        |

Java では `pom.xml` に「親子関係」「バージョン依存」「プラグイン設定」などをガッツリ書くのが前提です。

###  親プロジェクトにあるこの記述って何なのか

```xml
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-dependencies</artifactId>
  <version>${spring.boot.version}</version>
  <type>pom</type>
  <scope>import</scope>
</dependency>
```

これは：
> 子モジュールが `spring-boot-starter-web` などを **バージョン番号なしで使えるようにする共通設定**

つまり、「バージョンはここでまとめて管理するから、子モジュールでは書かなくていいよ」っていう Maven の仕組みです。

###  子モジュール（my-app-web）では何をしているの？

```xml
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
```

これは：
> 「このモジュールは Webアプリケーションとして起動するから、必要なライブラリ一式（Tomcat, Jackson, Spring MVCなど）を入れてね」という指定です。

実際、これ1行で以下を全部まとめてくれるのが「starter」方式のいいところです：

- Spring MVC
- Jackson（JSON変換）
- 内蔵Tomcat
- エラーハンドリング etc...


## Springの動き
**「どうして `/hello` にアクセスすると JSON が返ってくるのか？」** を、Spring Boot の仕組みを含めて記載します。


### 流れ

Spring Boot は、以下の流れで動いています：

1. ブラウザが `/hello` にアクセス
2. `HelloController.hello()` メソッドが呼ばれる
3. `new Greeting(...)` を返す
4. Spring Boot（Spring MVC）が Greeting オブジェクトを **自動で JSON に変換**
5. ブラウザに JSON として表示される


### 各パーツの役割を分解して解説


### 🔹 `@RestController`

```java
@RestController
public class HelloController {
```

- `@Controller` + `@ResponseBody` の省略形
- 「戻り値は**HTMLではなくJSONなどのデータ**として返します」という意味


### 🔹 `@GetMapping("/hello")`

```java
@GetMapping("/hello")
public Greeting hello() {
```

- `/hello` にアクセスが来たらこのメソッドを実行するというルーティング（URLマッピング）
- 戻り値に `Greeting` を返す（＝POJO）


### 🔹 `new Greeting(...)`

```java
return new Greeting("こんにちは、Spring Boot + マルチモジュール from WSL!");
```

- 単に `Greeting` クラスのインスタンスを作って返してるだけ

---

### 🔹 `Greeting` クラス

```java
public class Greeting {
    private String message;

    public Greeting(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}
```

- `message` フィールドを持っただけのデータクラス
- **getter（getMessage）を持っているのがポイント！**


### 🔹 自動で JSON にしてくれるのは誰？
Spring Boot に含まれる `spring-boot-starter-web` が内部で **Jackson** というライブラリを使っています。

- Javaオブジェクト → JSON文字列 に変換
- これは「シリアライズ」と呼ばれます

🔸 Jackson は以下のように変換します：

```java
new Greeting("こんにちは")
```

 こうなる

```json
{"message":"こんにちは"}
```

---

## Spring Bootの特徴だらけ

- `@RestController` をつけるだけで「返すものはJSONだな」と判断してくれる
- `@GetMapping("/hello")` でURLルーティング
- `Greeting` はただのJavaクラスでも、**getterがあれば勝手にJSONにしてくれる**
- 明示的に「JSONに変換しろ」って書かなくてもよい


## まとめ

| 概念               | 実装したもの             | Springの役割                     |
|--------------------|--------------------------|----------------------------------|
| URLに応じた処理     | `@GetMapping("/hello")`  | URLルーティング                  |
| コントローラー     | `@RestController`        | JSONとして返す                    |
| データを返す       | `return new Greeting(...)` | Javaオブジェクトとして返す       |
| JSONへの変換       | `Greeting` + `getter`    | Jackson による自動変換           |

---


## 前準備
### WSL設定
- WSL 上に Java（JDK 8 以上）と Maven がインストール済み

### maven
```bash
sudo apt update
sudo apt install maven
```

そして、優先パスを変えるか `.bashrc` や `.zshrc` に以下を追記：

```bash
export PATH="/usr/share/maven/bin:$PATH"
source ~/.bashrc
```

### Java
```bash
readlink -f $(which java)
```

例：`/usr/lib/jvm/java-17-openjdk-amd64/bin/java`

その後、以下を実行：

```bash
echo 'export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64' >> ~/.bashrc
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> ~/.bashrc
source ~/.bashrc
```