なんとな~くしあわせ?の日記

「そしてそれゆえ、知識そのものが力である」 (Nam et ipsa scientia potestas est.) 〜 フランシス・ベーコン

Kotlin+spring-bootでapplication.ymlのデータを読み込む

Kotlin+spring-bootでapplication.ymlのデータを読み込む

  • spring-bootでコンフィグファイルを外だしするとき、一般的にはapplication.propertiesやapplication.ymlに設定する
  • Kotlinで作成したプロジェクトで読み込む方法を調査した
  • Javaでも使えると思う

@ConfigurationPropertiesを使う

  • stackoverflow.com
    • @ConfigurationPropertiesを使い、YAMLの内容をデータクラスに注入する
    • prefixを指定することでYAMLのあるキーの下にある要素を取得できる
@Configuration
@ConfigurationProperties(prefix = "myconfig")
class MqttProperties {
    lateinit var myHost: String
    lateinit var myPort: String
    lateinit var myUser: String
    lateinit var myPass: String    
}
myconfig:
  my-host: ssl://example.com
  my-port: 23894
  my-user: user
  my-pass: pass
  • これは通常の用途ならば何も問題ないと思われる。簡単。
  • しかし、動的にbeanを登録したい場合、BeanDefinitionRegistryPostProcessorを使うのだが、それのメソッドが呼ばれた段階では@ConfigurationPropertiesでプロパティがロードされていない
  • @ConfigurationPropertiesがロードされるのはbeanが登録された後ぐらいみたいなんですよね…深くは調べてないけど

そのへんの話は以下
spring - Create N number of beans with BeanDefinitionRegistryPostProcessor - Stack Overflow

Binder APIを使用する

github.com

  • Wikiの記述にあるようにBinder APIは@ConfigurationPropertiesの枠組みの外側で動くことができる
  • BeanDefinitionRegistryPostProcessorを使う場合でもEnvironmentが読み込まれた段階でプロパティをロードできるので、よろしい
サンプルコード
myconfig:
  sample:
    key1: value1
    key2: value2
    key2: value3
@Configuration
class BindSampleConfig : EnvironmentAware {

    private lateinit var environment: Environment
    private lateinit var sample: Map<String, String>


    override fun setEnvironment(environment: Environment) {
        // EnvironmentAwareのメソッドをオーバーライド
        this.environment = environment
        // ここでspringのprofile別のコンフィグを読み分ける
        val binder = Binder.get(environment)

        this.sample = binder
                .bind("myconfig.sample", Bindable.mapOf(String::class.java, String::class.java))
                .orElseThrow { IllegalStateException() } // 読み込み失敗ならば例外、など
    }
}
  • Bindable.listOfとかBindable.mapOfとかが使えるので、型さえ合わせればいろいろ応用が利きそう