GradleでSpring Bootのプロジェクトを環境別にビルドする

Gradleで環境別にビルドを実行する方法はいくつかありますが、
今回はSpring Bootのapplication.propertiesの設定を書き換えてからwarファイルを出力する方法を紹介します。

使用する開発ツールはSTSで、Gradle Buildshipが動くことが前提となっています。

最終的には赤枠で囲った部分がGradle Tasksに追加され、
実行することでそれぞれの環境用のwarファイルを出力できるようになります。
![スクリーンショット 2019-10-07 22.02.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239351/a6ba2694-05a9-8fe3-96c8-7c787abedf29.png)


# 準備
[Spring Initializr](https://start.spring.io/
)でプロジェクトの雛形を作成します。
ProjectでGradle Projectを選択し、Packagingを選んでGenerateボタンを押してダウンロードします。
今回はwarを出力する想定で動かします。


ダウンロードが終わったらzipファイルを解凍して
STSにGradleプロジェクトとしてインポートしましょう。


# ディレクトリ構成

```
demo
│  build.gradle                     // ここにビルドスクリプトを追加する
│  application_template.properties  // 新しく作成する
│
├─.gradle
├─.settings
├─bin
├─gradle
└─src
    ├─main
    │  ├─java
    │  │  └─com
    │  │      └─example
    │  │          └─demo
    │  │                DemoApplication.java  // 修正する
    │  │
    │  └─resources
    │          application-dev.properties     // 新しく作成する
    │          application-product.properties // 新しく作成する
    │          application.properties         // 修正する
    │
    └─test
```

# 設定ファイル
Spring Bootではapplication-環境名.propertiesというファイルを用意し、
application.propertiesに環境名を設定することで環境別の設定を適用することができます。
ということで、resources配下に以下のような3つのファイルを用意してください。

```properties:application.properties
spring.profiles.active=dev
```

```properties:application-dev.properties
sample_data=Development Environment
```

```properties:application-product.properties
sample_data=Production Environment
```

# サンプルアプリケーション
今回は環境別の設定ファイルの内容を表示するというめっちゃシンプルなアプリを動かします。
以下のようにApplication.javaを書き換えましょう。


```java:DemoApplication.java
package com.example.demo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class DemoApplication {
	
	@Value("${sample_data}")
	private String data;  // この値が設定ファイルのsample_dataの値から読み込まれる

	public static void main(String[] args) {
		ConfigurableApplicationContext ctx = SpringApplication.run(DemoApplication.class, args);
		DemoApplication demo = ctx.getBean(DemoApplication.class);
		demo.run();
	}
	
	private void run() {
		System.out.println(data);
	}
}
```

実行するとapplication.propertiesで設定されたファイルが読み込まれ、その環境に応じた値が表示されるはずです。

```:実行結果（Springのログは略）
Development Environment
```

# ビルドスクリプトを書く
今回はSTSのGradle Tasksビューから環境別のタスクを実行しwarを出力します。
まずはapplication.propertiesをビルド対象の環境が設定されたものに置き換える処理を実行するようにします。
以下のようなapplication.propertiesの雛形のファイルを作成しましょう。

```properties:application_template.properties
spring.profiles.active=${buildEnv}
```

次にapplication.propertiesを雛形ファイルの${buildEnv}の部分を置き換えたファイルで上書きするタスクを記述します。
build.gradleに追記しましょう。
また、今回はwarコマンドを使用しますので、warコマンドの設定を記述するのも忘れないようにしておきましょう。

```gradle:build.gradle
plugins {
	// (中略)
}
// warコマンドの設定を追加
war {
	enabled = true
	archiveName 'sample.war'
}

// (中略)

// 各ファイル名
def templateFile = 'application_template.properties'
def configFile = 'application.properties'
// 対象の環境名（後に値を代入する）
def targetEnv = ''

task replaceConfig {
	description 'Replace application.properties environment.'

	doLast {
		println '　　　　start replacing :' + targetEnv
		// コピー実行部分
		copy {
			// from: コピーするのファイルのあるディレクトリを指定する
			from '.'
			// include: 今回はコピーするファイル名を指定している
			include templateFile
			// into: コピー先のディレクトリ
			into 'src/main/resources'
			// application.propertiesにリネームする
			rename(templateFile, configFile)
			
			// ${buildEnv}を置き換える
			expand([
				buildEnv: targetEnv
			])
			println "　　　　replacing finished"
		}
	}
	// 置き換えタスクが終了したらwarコマンドでwar出力する（jarファイルならjarにする）
	finalizedBy {
		war
	}
}

```
これでコピー処理実行後にwarタスクが実行されるようになりました。
ですが、これだけではまだtargetEnvの値が空文字なので設定する部分を記述します。
更に追記します。

```gradle:build.gradle

// （中略）

def templateFile = 'application_template.properties'
def configFile = 'application.properties'
def targetEnv = ''

task replaceConfig {
	// (中略)
}

task warProduct {
	// groupを設定することで Gradle Tasksビューのbuildの中に表示される
	group = 'build'
	description 'Create war file for product environment.'

	doLast {
		// targetEnvを設定する
		targetEnv = 'product'
	}
	// targetEnv設定後、replaceConfigタスクを実行する
	finalizedBy {
		replaceConfig
	}
}

// devもProductと同様
task warDev {
	group = 'build'
	description 'Create war file for dev environment.'
	
	doLast {
		targetEnv = 'dev'
	}
	finalizedBy {
		replaceConfig
	}

}
```

それぞれのタスク内でtargetEnvを設定後、replaceConfigタスクを実行するように記述しました。
これで動作するはずです。
以下はノーカット版です。

```gradle:build.gradle
plugins {
	id 'org.springframework.boot' version '2.1.9.RELEASE'
	id 'io.spring.dependency-management' version '1.0.8.RELEASE'
	id 'java'
	id 'war'
}
war {
	enabled = true
	archiveName 'sample.war'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

// 各ファイル名
def templateFile = 'application_template.properties'
def configFile = 'application.properties'
// 対象の環境名（後に値を代入する）
def targetEnv = ''

task replaceConfig {
	description 'Replace application.properties environment.'

	doLast {
		println '　　　　start replacing :' + targetEnv
		// コピー実行部分
		copy {
			// from: コピーするのファイルのあるディレクトリを指定する
			from '.'
			// include: 今回はコピーするファイル名を指定している
			include templateFile
			// コピー先のディレクトリ
			into 'src/main/resources'
			// application.propertiesにリネームする
			rename(templateFile, configFile)
			
			// application_template内の${buildEnv}を置き換える
			expand([
				buildEnv: targetEnv
			])
			println "　　　　replacing finished"
		}
	}
	// 置き換えタスクが終了したらwarコマンドでwar出力する
	finalizedBy {
		war
	}
}

task warProduct {
	// groupを設定することで Gradle Tasksビューのbuildの中に表示される
	group = 'build'
	description 'Create war file for product environment.'

	doLast {
		// targetEnvを設定する
		targetEnv = 'product'
	}
	// targetEnv設定後、replaceConfigタスクを実行する
	finalizedBy {
		replaceConfig
	}
}

// devもProductと同様
task warDev {
	group = 'build'
	description 'Create war file for dev environment.'
	
	doLast {
		targetEnv = 'dev'
	}
	finalizedBy {
		replaceConfig
	}
}
```

これで完成です。
Gradle TasksからwarDev、もしくはwarProductを実行するとbuild/lib内にwarファイルが作成されます。
warを解凍して、その中のapplication.propertiesを確認すると、各環境の設定になっているはずです。