きり丸の技術日記

技術・エンジニアのイベント・資格等はこちらにまとめる予定です

Javaで起動後DBの値をキャッシュに持つ等をPostConstructで処理させる(SpringBoot)

Javaのオープンチャットにて、「SpringBootの起動時にDBアクセスしてマスタデータを保持することは可能ですか?」といった質問が出てきました。

この記事は、解決方法として挙げられたPostConstructという処理を理解していなかったので、PostConstructを勉強するための記事です。

環境

  • Java
    • 15
  • org.springframework.boot
    • 2.4.5
  • Lombok
    • 1.18.20

※ DIするためにSpringBootを使用していますが、PostConstruct自体はJavaの機能なので、他のフレームワークでも使用できると思います。

ユースケース

  • アプリ起動後にDBのデータをキャッシュに持たせたい
  • アプリ起動時の設定ファイルの設定をログに出力したい
  • DBと設定ファイルが不正な状態の時にアプリ起動を中止させたい

要約

  • DI対象としてインスタンスを生成したタイミングでPostConstructを実行する
  • 単純なインスタンスを作成したタイミングでは実行しない

動作確認

設定ファイルのテスト方法に関しては、こちらの記事を参考にしてください。

プロダクションコード

URLを設定するための抽象クラスExternalPropertiesに、Facebookへの設定を読み込ませる具象クラスFacebookPropertiesを用意する。

@Getter
@Setter
@Slf4j
@ToString
public abstract class ExternalProperties {
  private String host;
  private String protocol;
  private String port;
  private String endpoint;
  private String timeout;

  public URI getUri() {
    return URI.create(protocol + "://" + host + ":" + port + "/" + endpoint);
  }

  @PostConstruct
  public void display() {
    log.info("********************");
    log.info(this.toString());
    log.info("********************");
  }

  @PostConstruct
  public void validate() {
    if (host == null) {
      throw new RuntimeException("設定されていない!");
    }
  }
}

@Getter
@Setter
@Configuration
@ConfigurationProperties(prefix = "external.facebook")
@ToString(callSuper = true)
public class FacebookProperties extends ExternalProperties {
}

テストコード

テストクラスを用意してPostConstructが呼ばれることを確認する。

@SpringJUnitConfig(initializers = ConfigDataApplicationContextInitializer.class)
@EnableConfigurationProperties({FacebookProperties.class})
class FacebookPropertiesTests {
  @Autowired
  FacebookProperties properties;

  @Test
  void AutowiredしないときはPostConstructが呼ばれない() {
    new FacebookProperties();

    // 起動ログを目視で確認する
    // エラーも発生しない
  }
}

EnableConfigurationPropertiesでDI対象としてFacebookPropertiesのインスタンスが生成されるタイミングで実行されました。

PostConstructという名称から、コンストラクタでのインスタンス生成後に呼ばれるかと思っておりましたが、new FacebookProperties()したタイミングでは、呼ばれることはありませんでした。

...ログ省略
14:52:22.700 [main] INFO kirimaru.config.ExternalProperties - ********************
14:52:22.873 [main] INFO kirimaru.config.ExternalProperties - FacebookProperties(super=ExternalProperties(host=localhost, protocol=http, port=10080, endpoint=facebook, timeout=3))
14:52:22.874 [main] INFO kirimaru.config.ExternalProperties - ********************
...ログ省略

備考

この機能を知ったうえでも、PostConstructを使用せずに、mainメソッドに書く方が分かりやすいのではないかと思っていました。

その件についても、オープンチャットに書いたところ以下の回答を得られました。

  • ユースケさんの回答
    • Springでインジェクトされるコンポーネントに依存しない処理ならそれでもいいですね。Springに用意してもらうDataSourceを使ったり、コンポーネントのフィールド変数においたりするにはPostConstruct使うのがいいですね
  • kisさんの回答
    • @PostConstructは必要な初期処理がそれぞれのクラスで書けて、mainで気にする必要が無いところですかねー

f:id:nainaistar:20210605154007p:plain

ソースコード

ExternalProperties.java github.com

テストコード: github.com

終わりに

職場では既にPostConstructを使っていたのですが、その方が既に現場にいないこともあって、なぜ使用しているかを聞くことができませんでした。

PostConstructを使用するとソースコード間の結合度が緩くなりすぎて処理が追えなくなるため、ソースコードの可読性を意識するのであればmain処理に書くべきでは?と思っていたこともあり、勉強していませんでした。

今回の件で、PostConstructを使用するユースケースを勉強することができたので、今後の設計レベルを一段階上げられるように出来そうです。


この記事がお役に立ちましたら、各種SNSでのシェアや、今後も情報発信しますのでフォローよろしくお願いします。

類似記事

SpringBootでpropertiesやymlの設定ファイルが読み込めることのテストを書く【Java】 nainaistar.hatenablog.com

【2021】SpringBootでpropertiesやymlの設定ファイルが読み込めることのテストを書く nainaistar.hatenablog.com

f:id:nainaistar:20210605153751p:plain