spring.properties 相当のファイルを増やす

概要

共通モジュールとそれを利用するモジュールを、同一 gradle プロジェクト内で開発したいときなど 共通モジュール用と利用側モジュールでそれぞれ設定ファイルを書きたい場合がある。

Spring Boot の設定について

Spring Bootでは 設定(プロパティ)を key=value で対応付されている。設定方法はいろいろあり、一覧としては下記のようになる。設定ファイルはこのうち 12, 13, 14 あたりが該当する。

f:id:nise_nabe:20170917161424p:plain

24. Externalized Configuration

具体的な実装を説明すると、設定のプロパティは Java コード上ではおもに org.springframework.core.env.PropertyResolver の実装クラス、つまり ConfigurablaeEnvironment クラスで表現されている。ConfigurableEnvironment 実装クラスは複数の PropertySource を登録することができるようになっている。application.properties は 何らかの方法によって Properties ファイルに変換され、 PropertiesPropertySource としてここに登録され、利用時にキーに対応したものを取得する処理を行っている。

spring.properties 相当のファイルを増やす方法

とりあえず自分の調べた限りだと下記3つぐらいの方法がありそう。

  • profile として共通モジュール用または利用モジュール用を定義する
    • 利点:楽
    • 欠点:さらに別の profile が設定に影響する場合が面倒
  • ApplicationContextInitializer を使って追加した設定ファイルをロードさせる
    • 利点:細かく制御できる
    • 欠点:実装を頑張る必要がある。 IDE サポートがなさそう。
  • spring.config.name をカンマ区切りで使う
    • 利点:標準の機能。良さそう。
    • 欠点:(自分の調べた範囲だと)ドキュメントになさそうで IDE サポートがいまのところない
profile として共通モジュール用または利用モジュール用を定義する

一応 profile として追加することで簡易的に対応する方法もある。ロード順でいうと 12, 13 に相当する部分を利用する。 ただしこの方法の場合、 application.properties のようにそれそのもの + 別の profile と組み合わせるような設定はできない。

ApplicationContextInitializer を使って追加した設定ファイルをロードさせる

標準の application.application(yml) については ConfigFileApplicationListener という ApplicatoinListener のクラスがあり、ApplicationEnvironmentPreparedEvent が発生したときに呼ばれるクラスでロードしている。

(実はクラスは EnvironmentPostProcessor を動かすクラスでもあるため設定後に EnvironmentPostProcess として Factories に 登録しておくことによって設定を書き換えるということができる。)

コードを見ると SpringApplication では Environment 初期化 → ApplicationContext 初期化という順番で処理をしており、Environment 初期化時または、初期化後の何らかのタイミングで Environment の設定に上書きするという処理を入れるとうまくいきそう。コード上をみると以下の方法がありそうということがわかる。

タイミング1: SpringApplicationRunListener#environmentPrepared() で拾う。

タイミング2: EnviromnebtPostProcessor#postProcessEnvironment() で拾う。

タイミング3: ApplicationContextInitializer を使う

タイミング4: SpringApplicationRunListener#contextPrepared() で拾う。

Spring Boot で書かれている github 上のドキュメントには下記のように書かれている。

spring-boot/howto.adoc at v1.5.6.RELEASE · spring-projects/spring-boot · GitHub

  • Programmatically per application by calling the addListeners and addInitializers methods on SpringApplicationbefore you run it.
  • Declaratively per application by setting context.initializer.classes or context.listener.classes.
  • Declaratively for all applications by adding a META-INF/spring.factories and packaging a jar file that the applications all use as a library.

ドキュメントでは タイミング3 について書かれているように見える。 ここで設定ファイルをロードする処理を自分で書くとよさそう。

(Initializer を使ったコード例は時間がないので一旦省略)

spring.config.name をカンマ区切りで使う

spring.config.name はデフォルトの application という名前のファイル名を変更する際に利用する、ようにみえる。しかし実装を見てみると spring.config.name を カンマで区切って指定すると複数の設定ファイルを読むようになる。後に書かれたほうが強い。これをカンマ区切りで書くとよさそう。

https://github.com/spring-projects/spring-boot/blob/v1.5.6.RELEASE/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java#L639...L653

ただし spring.config.name は設定ファイルを読み込む前に設定しないと意味がない。

24. Externalized Configuration

spring.config.name and spring.config.location are used very early to determine which files have to be loaded so they have to be defined as an environment property (typically OS env, system property or command line argument).

設定コード例

SpringApplication を使う場合。

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(MyApplication.class);
        Map<String, Object> defaultProperties = new HashMap<>();
        defaultProperties.put("spring.config.name", "application,myapplication");
        application.setDefaultProperties(defaultProperties);
        application.run(args);
    }

SpringApplicationBuilder を使う場合。

    public static void main(String[] args) {
        new SpringApplicationBuilder(MyApplication.class)
                .properties("spring.config.name=application,myapplication")
                .run(args);

    }

ちなみに SpringApplication#defaultProperties() や SpringApplicationBuilder#properties() などで登録した値は MapPropertySource として ConfigurableEnvironment に登録される。優先順位としては一番最後となる。

IDE での spring.config.name の設定(Intellij IDEA)

結論からいうと現状では変更はできるが複数設定はできない様子。

spring.config.name は下記のブログにあるように Intellij IDEA 側の設定を変更することで、追加した設定ファイル上でも spring.properties の補完が動くなどのサポートが得られる。

IntelliJ IDEA 2017.2: Spring Boot Improvements | IntelliJ IDEA Blog

File > Project Structure の中の Facets 以下に下記のように "Customize Spring Boot" とある部分をクリック

f:id:nise_nabe:20170917153502p:plain

すると下記のように spring.config.name を設定するところが出る。ここを編集すると、一応既存の 設定ファイル名を「書き換える」ことはできる。

f:id:nise_nabe:20170917153726p:plain

ただし、現状ではここは spring.config.name のように , 区切りで入力しても反映されない。Spring Boot のドキュメントにも記載されてないのでそれはそうかなという気はしなくもないが、まあ対応してくれると便利ですね。

とりあえずは当初の目的通り モジュールそれぞれで applicatoin.yml と myapplication.yml を持つことを想定し、モジュールごとに上記設定をすると一応は解決となる。

以上。