Spring Bootの外部設定

外部設定(Externalized Configuration)の利用について説明する。

プロパティファイル(properties)

プロパティファイルの使用

基本

フォルダー構成

|-build.gradle
`-src/main/
  |-java/sample/springboot/
  |  `-Main.java
  `-resources/
    `-application.properties

Main.java

package sample.springboot;

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 Main {

    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            Main m = ctx.getBean(Main.class);
            m.hello();
        }
    }

    @Value("${sample.value}")
    private String value;

    public void hello() {
        System.out.println("sample.value = " + this.value);
    }
}

実行結果

sample.value = Hello Properties File!!
  • クラスパス配下に application.properties というプロパティファイルを配置します。
  • するとSpring Bootが自動的にそのファイルを読み込みます。
  • プロパティファイルの値は @Value アノテーションを使ってBeanに注入できます。
  • ${プロパティ名} 形式で取得したい値を指定します。
ファイルを置く場所

プロパティファイルを置く場所には、いくつかの読み込み優先順位があります。

  1. 起動時に --spring.config.location で指定したファイル。
  2. 現在ディレクトリ直下の config ディレクトリにあるファイル。
  3. 現在ディレクトリにあるファイル。
  4. クラスパス直下の config パッケージにあるファイル。
  5. クラスパス直下にあるファイル。

数字が小さいほど優先順位が高くなります。優先順位の低い設定は、上位の設定で上書きされます。

フォルダー構成(jar内部)

|-application.properties
|-config/
|  `-application.properties
`-sample/springboot/
   `-Main.class

フォルダー構成(実行時)

|-application.properties
|-other.properties
|-config/
| `-application.properties
`-build/libs/
  `-spring-boot-sample.jar

other.properties

value5=other

application.properties(現在ディレクトリのconfigディレクトリ配下)

value4=currentdir/config
value5=currentdir/config

application.properties(現在ディレクトリ配下)

value3=currentdir/
value4=currentdir/
value5=currentdir/

application.properties(クラスパスのconfigパッケージ配下)

value2=classpath/config
value3=classpath/config
value4=classpath/config
value5=classpath/config

application.properties(クラスパス直下)

value1=classpath/
value2=classpath/
value3=classpath/
value4=classpath/
value5=classpath/

Main.java

package sample.springboot;

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 Main {

    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            Main m = ctx.getBean(Main.class);
            m.hello();
        }
    }

    @Value("${value1}") private String value1;
    @Value("${value2}") private String value2;
    @Value("${value3}") private String value3;
    @Value("${value4}") private String value4;
    @Value("${value5}") private String value5;

    public void hello() {
        System.out.println("value1=" + value1);
        System.out.println("value2=" + value2);
        System.out.println("value3=" + value3);
        System.out.println("value4=" + value4);
        System.out.println("value5=" + value5);
    }
}

実行

$ java -jar build/libs/spring-boot-sample.jar --spring.config.location=other.properties

value1=classpath/
value2=classpath/config
value3=currentdir/
value4=currentdir/config
value5=other

優先順位に従って設定が上書きされています。

プロファイル指定

フォルダー構成

|-application.properties
|-application-develop.properties
`-build/libs/
   `-spring-boot-sample.jar

application.properties

value=release module

application-develop.properties

value=develop module

実行

$ java -jar build/libs/spring-boot-sample.jar

value=release module

$ java -jar build/libs/spring-boot-sample.jar --spring.profiles.active=develop

value=develop module
  • プロパティファイルを application-{プロファイル名}.properties 形式で作成します。
  • コマンドライン引数で spring.profiles.active に使用するプロファイル名を指定します。
    • コマンドライン引数以外にも、システムプロパティやOS環境変数でも指定できます。
  • すると、指定したプロファイルに対応するプロパティファイルが読み込まれます。

同じ接頭辞を持つプロパティをBeanにマッピングする

コード作成

application.properties

person.firstName=Sato
person.last-name=Taro
person.age=18

Person.java

package sample.springboot;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix="person")
public class Person {

    private String firstName;
    private String lastName;
    private int age;

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void hello() {
        System.out.println(firstName + " " + lastName + " : " + age);
    }
}

Main.java

package sample.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
@EnableConfigurationProperties
public class Main {

    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            Person person = ctx.getBean(Person.class);
            person.hello();
        }
    }
}

実行結果

Sato Taro : 18
  • @ConfigurationProperties を使うと、特定の接頭辞を持つプロパティをBeanにマッピングできます。
    • Beanにはsetterメソッドが必要です。
    • フィールド名はキャメルケースだけでなく、ハイフン (-) 区切りやアンダースコア (_) の場合にもマッピングされます。
  • この仕組みを有効にするには、@EnableConfigurationProperties アノテーションを追加する必要があります。
    • 厳密には、@Configuration アノテーションが付与されたクラスに追加します。

YAMLを使用する

設定ファイルを application.yaml にすると、YAMLを使用できます。

基本マッピング

application.yaml

aaa:
    bbb:
        ccc: Hoge
        ddd: Fuga
    eee:
        fff: Piyo

Main.java

package sample.springboot;

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 Main {

    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            Main m = ctx.getBean(Main.class);
            m.hello();
        }
    }

    @Value("${aaa.bbb.ccc}") private String ccc;
    @Value("${aaa.bbb.ddd}") private String ddd;
    @Value("${aaa.eee.fff}") private String fff;

    public void hello() {
        System.out.println("ccc=" + ccc);
        System.out.println("ddd=" + ddd);
        System.out.println("fff=" + fff);
    }
}

実行結果

ccc=Hoge
ddd=Fuga
fff=Piyo

リストマッピング

application.yaml

myconf:
    list:
        - hoge
        - fuga
        - piyo

MyConf.java

package sample.springboot;

import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix="myconf")
public class MyConfig {

    private List<String> list;

    public List<String> getList() {
        return list;
    }

    public void setList(List<String> list) {
        this.list = list;
    }
}

Main.java

package sample.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
@EnableConfigurationProperties
public class Main {

    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            MyConfig conf = ctx.getBean(MyConfig.class);
            System.out.println(conf.getList());
        }
    }
}

実行結果

[hoge, fuga, piyo]
  • Beanへのマッピングを使うと、Listへのマッピングも可能になります。

プロパティファイル以外の設定値を渡す

Main.java

package sample.springboot;

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 Main {

    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            Main m = ctx.getBean(Main.class);
            m.hello();
        }
    }

    @Value("${value}") private String value;

    public void hello() {
        System.out.println("value=" + value);
    }
}

コマンドライン引数

$ java -jar build/libs/spring-boot-sample.jar --value=commandline

value=commandline
  • --[プロパティ名]=[値] で、コマンドライン引数から設定値を渡します。

Javaシステムプロパティ

$ java -Dvalue=systemproperty -jar build/libs/spring-boot-sample.jar

value=systemproperty
  • --D [プロパティ名]=[値] で、システムプロパティから設定値を渡します。

OS環境変数

$ set value=osenvironment

$ java -jar build/libs/spring-boot-sample.jar

value=osenvironment

この例のOSはWindowsです。

デフォルトプロパティ

Main.java

package sample.springboot;

import java.util.HashMap;
import java.util.Map;

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 Main {

    public static void main(String[] args) {
        Map<String, Object> properties = new HashMap<>();
        properties.put("value", "default property");

        SpringApplication app = new SpringApplication(Main.class);
        app.setDefaultProperties(properties);

        try (ConfigurableApplicationContext ctx = app.run(args)) {
            Main m = ctx.getBean(Main.class);
            m.hello();
        }
    }

    @Value("${value}") private String value;

    public void hello() {
        System.out.println("value=" + value);
    }
}

実行結果

value=default property

SpringApplication#setDefaultProperties(Map<String, Object>) でデフォルト設定を指定できます。

プロパティの優先順位

プロパティファイルの場合と同じように、設定値の渡し方には優先順位があり、優先順位が高い方法が低い方法で指定された設定値を上書きします。

優先順位は次のとおりです。

  1. コマンドライン引数
  2. JNDIの java:comp/env から取得したプロパティ
  3. システムプロパティ
  4. OS環境変数
  5. jar外部のプロファイル指定プロパティファイル
  6. jar内部のプロファイル指定プロパティファイル
  7. jar外部にあるプロパティファイル
  8. jar内部にあるプロパティファイル
  9. @PropertySource で指定されたプロパティファイル
  10. デフォルトプロパティ

数字が小さいほど優先順位が高くなります。