Spring Bootで、環境ごとに異なる値を持つモデルクラスを作るオススメ方法

こんにちは。 エキサイト株式会社の三浦です。

コードを書いていると、たまに環境ごとに異なる値を使いたくなるときがあります。 例えば、開発環境と本番環境で使用するDBが異なる場合は、DBのURLは環境ごとに異なるはずです。

今回は、Spring Bootでそのような環境ごとに異なる値をモデルクラス内で使いたい場合の、オススメの方法を紹介します。

環境ごとに異なる値

コードを書く時、たまに環境ごとに異なる値を使いたくなることがあります。 Spring Bootでは、そのような値を扱う方法の1つとして、 application.yml というものを提供しています。

例えば、 local 環境と prod 環境で別々の値を持ちたい時は、

application-local.yml

data_1: local_value

application-prod.yml

data_1: prod_value

というファイルを用意し、実行時にファイル名をそれぞれ指定すれば、コード内で data_1 を呼び出すと、指定したファイルの値を取得することができるようになるのです。

この application.yml 内のデータを、モデルクラスで使いたい場合があるとしましょう。 以下のような場合です。

public class SampleImageModel {
    /**
     * 画像ドメイン
     * TODO: ここに、application.ymlから値を持ってきたい
     */
    private String imageDomain;

    /**
     * 画像パス
     * この値は、DB等からとってきてそのまま入れる
     */
    private String imagePath;

    /**
     * 画像URLを取得する
     * @return 画像URL
     */
    public String getImageUrl() {
        return this.imageDomain + this.imagePath;
    }
}

方法はもちろんいくつかありますが、個人的にオススメの方法を紹介します。

オススメ取得方法

application.ymlの準備

まずは application.yml を環境ごとに用意します。 ここまでは同じです。

image:
  domain: https://sample/

コードからapplication.ymlのデータを呼び出す

続いて、コードから以下の方法で application.yml を呼び出します。

package sample;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;

@ConstructorBinding
@ConfigurationProperties(prefix = "image")
@RequiredArgsConstructor
public class SampleImageConfig {
    /**
     * 画像ドメイン
     */
    @Getter
    private final String domain;
}

取得方法は他にもありますが、こうすることで、例えばスキーマは別管理にしたいときに、

package sample;

import lombok.RequiredArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;

@ConstructorBinding
@ConfigurationProperties(prefix = "image")
@RequiredArgsConstructor
public class SampleImageConfig {
    /**
     * 画像ドメイン
     */
    private final String domain;

    public String getDomain() {
        return "https://" + domain;
    }
}

こうできるなど、より柔軟性をあげることができたりします。

モデルクラスにデータを入れる

最後に、モデルクラスにデータを入れるには、Factoryクラスを使用します。

まずはモデルクラスで、コンストラクタからデータを入れられるようにします。

package sample;

import lombok.Data;
import lombok.experimental.Accessors;

@Data
@Accessors(chain = true)
public class SampleImageModel {
    /**
     * 画像ドメイン
     * TODO: ここに、application.ymlから値を持ってきたい
     */
    private final String imageDomain;

    public SampleImageModel(String imageDomain) {
        this.imageDomain = imageDomain;
    }

    /**
     * 画像パス
     * この値は、DB等からとってきてそのまま入れる
     */
    private String imagePath;

    /**
     * 画像URLを取得する
     * @return 画像URL
     */
    public String getImageUrl() {
        return this.imageDomain + this.imagePath;
    }
}

その上で、Factoryクラスを作成します。

package sample;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class SampleImageModelFactory {
    private final SampleImageConfig sampleImageConfig;

    public SampleImageModel create() {
        return new SampleImageModel(this.sampleImageConfig.getDomain());
    }
}

これを作れば、後はこのモデルを生成したい時は、以下のようにすれば簡単に作れるようになります。

// SampleImageModelFactoryはDIして使う
SampleImageModel sampleImageModel = sampleImageModelFactory.create().setImagePath("sample/image.jpg");

この方法を取ることで、「ドメインをあらかじめ application.yml から持ってくる」などのことを考えずにモデルクラス使えるようになります。

最後に

今回は個人的なオススメ方法を紹介しましたが、あくまで個人的な考えのものであり、もしかしたらもっと良い方法があるかもしれません。 あくまで一例として参考にしていただければと思います。