Help us understand the problem. What is going on with this article?

Spring boot : build.gradle と application.yml で同じ値を参照したい。

やりたいこと

build.gradle と application.yml もとい、Javaプロセス内の両方で使いたい変数が色々ありまして ( DB接続先 とか S3接続先 とか ) 、その記述を一箇所にまとめて参照する。

二箇所以上に記述を分散させてしまうと、環境 ( local や production ) によって切り替えたい時の変更が煩雑になったり、単純に修正箇所が増えてヒューマンエラーにつながったりします。
実際に適当に作って面倒になってきたり、複数人で作っていて似たような定義が出来上がってしまって痛い目を見ました。

つまり

  • build.gradle、application.yml ( 及びapplication実行時 ) に共通で使える property 定義であること
  • ビルド環境によって値を「簡単」切り替えられる
  • 一箇所に定義して記述が「簡単」

ということがやりたかったわけです。
ちなみに「簡単」という条件を除けば以前の投稿の方法でも条件を満たしていました。

しかし以前の方法だと property が増えると修正しなければならない箇所が増えて面倒でした。今回はその面倒くささを克服したものになります。

環境

  • Spring boot 2.1.1.RELEASE
  • gradle 4.10.3

実装

Propertyファイルを用意する

先ずは property を定義したファイルを用意します。
Key = value の形で定義します。

default.config.gradle
applicationName = "hogehoge"
applicationVersion = "1.0.0"

datasourceUrl = "jdbc:mysql://localhost:13306/hogehoge?useSSL=false"
datasourceUser = "root"
datasourcePassword = "root"
datasourceSchemas = "hogehoge"

s3_accessKey = "test"
s3_secretKey = "test"
s3_serviceEndpoint = "./local"
s3_region = "hoge"

環境ごとの Property を用意する

[環境名(productionとか)].config.properties の形でファイル名を定義しておきます。
こちらは default と比べて上書きしたい property 分のみ記述すれば大丈夫です。

datasourceUrl = System.getenv('DATA_SOURCE_HOST') ?: ""
datasourceUser = System.getenv('DATA_SOURCE_USER') ?: ""
datasourcePassword = System.getenv('DATA_SOURCE_PASSWORD') ?: ""
datasourceSchemas = System.getenv('DATA_SOURCE_SCHEMAS') ?: ""

s3_accessKey = System.getenv("S3_ACCESS_KEY") ?: ""
secretKey = System.getenv("S3_SECRET_KEY") ?: ""
s3_serviceEndpoint = System.getenv("S3_SERVICE_END_POINT") ?: ""
s3_region = System.getenv("S3_REGION") ?: ""

こちらはSystem.getenv() で環境変数から取得するようにしています。

application.yml

置き換えたい property の値の Key を @ で括ったものを設定します。

application.yml
spring:
  profiles:
    active: local, development
  # MySQL接続情報
  datasource:
    url: @datasourceUrl@
    username: @datasourceUser@
    password: @datasourcePassword@

app:
  name: @applicationName@
  version: @applicationVersion@

appConfig:
  s3:
    accessKey: @s3_accessKey@
    secretKey: @s3_secretKey@
    serviceEndpoint: @s3_serviceEndpoint@
    region: @s3_region@

build.properties で export する

※関係箇所のみ記述

build.gradle
// 1. defaultのプロパティを読み込む
def props = new ConfigSlurper().parse(new File("$project.projectDir/default.config.gradle").toURI().toURL())

// 2. 環境を指定してpropetiesを上書きする
if (hasProperty('env')) {
  def envConfig = new ConfigSlurper().parse(new File("$project.projectDir/${env}.config.gradle").toURI().toURL())
  props = props + envConfig
}

// 3. application.ymlのプロパティを上書きする
processResources {
  filesMatching('**/application.yml') {
    filter(
        ReplaceTokens,
        tokens: props
    )
  }
}

ConfigSlurper()

Groovy のメソッドで property を記述した default.config.gradle のような感じで定義しておくと読み込んでアクセスできるようにしてくれるやつです。
今回はシンプルな書き方にしてますが、ドット区切りで改装指定したりと柔軟な書き方が可能です。

hasProperty('env')

これはビルド引数に env が指定されているかを確認しています。 ./gradlew build -P env=production のようにして指定します。

環境を指定してpropetiesを上書きする ところ

同じ用に ConfigSlurper() で該当環境と同じファイル名の property ファイルを読み込んで結合しています。 + で結合することによって Key が同じ値が上書きされます。

application.ymlのプロパティを上書きする ところ

  • filesMatching('**/application.yml') でapplication.ymlとう名前にマッチするファイルすべてを対象にしています。
  • filter() で一行ずつ置換します。 第1引数の ReplaceTokens は ant API で、第2引数の tokens で対象文字列を置き換える(KVS → [key: value, key2: value2]の形であればよい) 第2引数の propsはConfigSlurper()で読み込んだ値が KVS の形で入っている

application 内で property を使う設定

こちらは application.yml をjava側から読み込む方法を他の方々が書かれているのでそちらをご参照ください。

これで clean build してあげればお終い

おわり

最初は延々と誰か他にやっている人がいないか探していたんですが、なかなかピンポイントの記事が見つからず途方に暮れていました。しかし、よくよく考えてみたら Gradle は Groovy で書かれているので、 Groovy で出来ることはなんでもできる事に気づいてこの方法にたどり着きました。
Gradle を 検索ワードにして探していたときは見つからなかったことも Groovy に対して検索するとあっという間に見つかり、またやれることが広がった気がします。

詰まったら視点を変えてみる。精進が足りない。。。

omake : サブモジュールのビルド時でも同じ値を使いまわす

  • rootディレクトリの build.gradle で config を読み込む。
  • props(変数名は何でもいい) を定義して代入しているのでサブモジュール側でアクセスする
  • アクセスする方法は サブモジュール側の build.gradle で rootProject.ext.props で見れる。
  • application.yml への export はよしなに。自分は置換したい値のあるサブモジュール側の build.gradle に filesMatching()を書いた。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした