すきま風

勉強したことのメモとか

Spring Boot起動時に環境毎の設定ファイルを利用する方法 3パターン

Spring Boot × Gradleで起動時に読み込む設定ファイルを切り替える方法を3種類記載します。

方法の概説

  1. spring.profile.activeで起動時のapplication.yml (application.properties) を指定
  2. build時に環境毎の設定ファイルディレクトリをclass pathに追加
  3. 起動時にclass pathを追加

システム構成は以下リンクを参照ください。 ただし1, 2についてはマルチプロジェクト構成である必要はなく、env層も不要です。

1.spring.profiles.activeで起動時のapplication.yml (application.properties) を指定

spring.profiles.activeで起動時の設定ファイルを指定する方法です。 IDEでの起動時にはdefaultになります。 application.ymlで設定が完結している場合、この方法が一番シンプルです。

構成

└── src
    ├── main
    │   └── resources
    │       ├── application-common.yml
    │       ├── application-development.yml
    │       ├── application-local.yml
    │       ├── application.yml
    │       └── mybatis
    │           └── sql
    │               ├── mysql

application.yml

spring.profiles: default
spring.profiles.active: default
spring.profiles.include:
  - development
  - local # 最後に置いたもので設定を上書きする

application-development.yml

spring.profiles: development
spring.profiles.include:
  - common

spring:
  datasource:
# 以下、datasourceの設定...

# Logger設定
logging:
  level:
    org.springframework.web.servlet.PageNotFound: ERROR
    org.springframework.transaction: INFO

application-common.yml

# 環境に依存しない共通設定を記載する場所

application-local.yml

# 個人の開発で利用する場所
logging:
  level:
    org.springframework.transaction: TRACE

IDEでの起動時はapplication.ymlが利用されます。今回の例ではtransactionのlogging levelがTRACEになります。
application-development.ymlで起動する場合は、executable jarの起動時に以下のように指定すればOK。

java -Dspring.profiles.active=development -jar demo-web-0.0.1-SNAPSHOT.jar

2.build時に環境毎の設定ファイルディレクトリをclass pathに追加

設定ファイルが複数の場合、gradle build時にclass pathを追加する方法が有効です。

構成

└── src
    ├── main
    │   └── resources
    │       ├── application-common.yml
    │       ├── application-development.yml
    │       ├── application-local.yml
    │       └── mybatis
    │           └── sql
    └── profile
        ├── development
        │   └── application.yml
        └── local
            └── application.yml

src直下にprofile directoryを用意して、配下にapplication.ymlを置きます。 その他、環境に依存する設定があればprofileに追加していきます。環境に依存しないSQLファイルなどはsrc/main/resourcesに置きます。

development/application.yml

spring.profiles.active: development

local/application.yml

spring.profiles: default
spring.profiles.active: default
spring.profiles.include:
  - development
  - local # 最後に上書きしたものが有効になる

起動時にclass pathが追加になるようにbuild.gradleに設定します。

subprojects {
    // ...前略
    sourceSets {
        // profileごとのresourceを追加
        def envProfile =
            project.hasProperty('env_profile') ? project.properties['env_profile'] : 'local'

        main.resources {
            srcDirs "src/profile/$envProfile"
        }
    }
    // 後略...
}

デフォルト起動時はprofile/local が採用されます。build時にpathを変える場合はgradleのPオプションを使用します。

./gradlew bootJar -Penv_profile=development

あとは普通にexecutable jarを起動すればOK

3. 起動時にclass pathを追加

build時に設定ファイルをexecutable jarから外しておき、起動時に外部class pathに追加します。

構成

└── src
    ├── main
    │   └── resources
    │       ├── application.yml
    │       └── mybatis
    │           └── sql
    └── profile
        ├── development
        │   └── application.yml

この方法の場合、設定ファイルは独立したprojectに用意する必要があります。env層を用意して、設定ファイルはすべてここに置きます。 デフォルトではsrc/main/resources/application.ymlが読まれます。

executable jarにenv層を取り込まないようにし、またPropertiesLauncherを入れて外部class pathを追加できるようにします。

demo-web/build.gradle

bootJar {
    mainClassName = 'com.example.demo.DemoApplicationKt'

    // daemon化する
    launchScript()

    // loader.propertiesでクラスパスを追加するためにPropertiesLauncherを使う
    manifest {
        attributes 'Main-Class': 'org.springframework.boot.loader.PropertiesLauncher'
    }
}

// env層をjarから除去する
configurations {
    runtime.exclude module: 'demo-env'
}

src/profileのapplication.yml 等の設定ファイルを利用するために、release用のtaskを書きます

task release(dependsOn: ['demo-web:build']) {
    // build用ディレクトリ
    def applicationDir = new File(buildDir, 'application')

    doFirst {
        description 'build配下にapplication directoryを作成する'

        // 古いディレクトリが存在していたら削除
        if (applicationDir.exists()) {
            delete(applicationDir)
        }

        // build用directory作成
        applicationDir.mkdirs()
    }
    doLast {
        // demo-webで作成したjarファイルをapplicationDirにcopyする
        description 'application directoryにdemo-webのjarファイルをcopyする'
        project(':demo-web') {
            copy {
                from "${buildDir}/libs"
                into applicationDir
                include("*.jar")
            }
        }
    }

    doLast {
        description 'application directoryにdemo-envのresourceファイルをcopyする'

        // demo/build/application/resources
        def applicationResourcesDir = new File(applicationDir, 'resources')

        // develop / staging / production など環境を取得
        // -P optionだとproject.getProperty, -D optionだとSystem.getProperty
        def envProfile =
            project.hasProperty("env_profile") ? project.properties["env_profile"] : 'local'

        println("build profile is ${envProfile}")

        project(':demo-env') {
            copy {
                from "${projectDir}/src/main/resources"
                into applicationResourcesDir
            }

            // envのprofileで上書きをする
            if (envProfile != 'local') {
                String profileDir = "${projectDir}/profile/${envProfile}"

                if (!file(profileDir).exists()) {
                    throw new GradleException(profileDir + " is not found")
                }

                copy {
                    from "${projectDir}/profile/${envProfile}"
                    into applicationResourcesDir
                }
            }
        }
    }
}

profile 以下のファイルでsrc/main/resourcesのファイルを上書きコピーしています。
build時にはprofileを指定します。

./gradlew -Penv_profile=development release

demo/build以下にapplication directoryができて、その下にresourcesとexecutable jarができます。

├── application
│   ├── demo-web-0.0.1-SNAPSHOT.jar
│   └── resources

起動時にloader.path=resourcesを指定して、class path にresourcesを追加します。

java -Dloader.path=resources -jar demo-web-0.0.1-SNAPSHOT.jar

手間のかかる方法ですが、build後にresourceファイルを修正できる利点があります。

まとめ

gradleを利用した環境毎の設定ファイル読み込み方法をまとめました。
個人的には1 > 2 > 3の順でおすすめです。
(しかし会社で採用している方法は3だったりします ;-))