Gradle で Spring Boot 2 のマルチプロジェクト

Gradle を使って Spring Boot 2 のマルチプロジェクトを作る方法。

# 環境
**Java**

`openjdk 10.0.2 2018-07-17`

**Gradle**

`4.10`

**Spring Boot**

`2.0.4`


# 対象のマルチプロジェクト
```text
|-settings.gradle
|-build.gradle
|
|-foo/
| |-build.gradle
| :
|
`-bar/
  |-build.gradle
  :
```

- `foo` プロジェクトが `bar` プロジェクトを参照している
- 実装内容は今回関係ないので省略


# ダメパターンの設定
```groovy:/build.gradle
buildscript {
    ext {
		springBootVersion = '2.0.4.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

subprojects {
    apply plugin: "java"
    apply plugin: 'org.springframework.boot'
    apply plugin: 'io.spring.dependency-management'

    repositories {
        mavenCentral()
    }

    sourceCompatibility = 10
    targetCompatibility = 10
}
```

- サブプロジェクト全体 (`foo`, `bar`) に共通でプラグインなどを設定している。
- `io.spring.dependency-management` は、　Maven の BOM を利用して依存するライブラリのバージョンを制御する仕組みを Gradle でも使えるようにしたやつ
    - 参考：[Gradle で BOM を使いたいときには Spring チームの出している Dependency management plugin を使うのがよさそう - なにか作る](http://create-something.hatenadiary.jp/entry/2015/05/08/063000)
    - Spring Boot 関係の `dependencies` のバージョン指定を省略できるようになる
    - 1.x の頃は `org.springframework.boot` が内部で利用していたが、 2.x では外に出されたっぽい

```groovy:/foo/build.gradle
dependencies {
    compile project(":bar")
    compile "org.springframework.boot:spring-boot-starter-web"
}
```

- `:bar` への依存と、 `spring-boot-starter-web` への依存を宣言

## コンパイル結果
```text
> gradle :foo:compileJava

> Task :foo:compileJava FAILED
...\Foo.java:12: エラー: シンボルを見つけられません
        return new Bar().bar();
                        ^
  シンボル:   メソッド bar()
  場所: クラス Bar
エラー1個

FAILURE: Build failed with an exception.
```

`bar` プロジェクトのクラスが解決できずにエラーになる。

## 説明
- `bar` プロジェクトのビルド結果を確認してみる

```
> dir /b bar\build\
classes
tmp
```

- `bar` プロジェクトの jar が生成されていない
    - 1.x なら、 `bar\build\libs` の下に `bar.jar` という jar ファイルが出力されており、 `foo` プロジェクトはその jar を参照するようになっていた気がする
- なんか、 2.x からはデフォルトで `jar` プラグインの `enabled` が `false` に設定されるようになったらしい
    - https://docs.spring.io/spring-boot/docs/current/gradle-plugin/reference/html/#packaging-executable-and-normal
    - `jar` タスクでは jar ファイルは生成されず、 `bootJar` タスクで生成されるようになったらしい

# 対処方法
[java - Spring Boot multi module project with Gradle doesn't build - Stack Overflow](https://stackoverflow.com/questions/47598848/spring-boot-multi-module-project-with-gradle-doesnt-build)

- ここでは、 `jar.enabled` に `true` を設定すればええでという方法が紹介されている
- たしかに、これを設定すればビルドできるようになる

[Gradle multi-project build dependencies of subprojects cannot be resolved · Issue #11594 · spring-projects/spring-boot](https://github.com/spring-projects/spring-boot/issues/11594)

- ただ、公式の GitHub Issue の方を見ると、そもそも依存されている側のサブプロジェクト（`bar` プロジェクト）の方は Spring Boot プロジェクトではないのだから、サブプロジェクトにまで `org.springframework.boot` プラグインを適用することが間違っていると説明されている
- 確かにその通りだと思うので、こっちの方法で修正する

# OK パターン
```groovy:/build.gradle
buildscript {
    ext {
        springBootVersion = '2.0.4.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

subprojects {
    apply plugin: "java"
    apply plugin: "io.spring.dependency-management"

    repositories {
        mavenCentral()
    }

    dependencyManagement {
        imports {
            mavenBom "org.springframework.boot:spring-boot-dependencies:$springBootVersion"
        }
    }

    sourceCompatibility = 10
    targetCompatibility = 10
}
```

```groovy:/foo/build.gradle
apply plugin: "org.springframework.boot"

dependencies {
    compile project(":bar")
    compile "org.springframework.boot:spring-boot-starter-web"
}
```

## コンパイル結果
```
> gradle :foo:compileJava
BUILD SUCCESSFUL in 17s
```

## 説明
```groovy:/build.gradle
buildscript {
    ext {
        springBootVersion = '2.0.4.RELEASE'
    }
    ...
}

subprojects {
    apply plugin: "java"
    apply plugin: "io.spring.dependency-management"

    ...

    dependencyManagement {
        imports {
            mavenBom "org.springframework.boot:spring-boot-dependencies:$springBootVersion"
        }
    }

    ...
}
```

- 全てのサブプロジェクトに `org.springframework.boot` を設定するのをやめる
- そして、 `dependencyManagement` を追加して Spring Boot の BOM を読み込む

```groovy:/foo/build.gradle
apply plugin: "org.springframework.boot"

...
```

- `foo` プロジェクトのほうにだけ `org.springframework.boot` を適用する