Spring Bootで@ConfigurationPropertiesを使って設定を読み込む

application.yaml の設定値が増えてくると、@Value をあちこちに書くのが少しつらくなってきます。

単発の設定なら問題ないのですが、関連する設定が増えると、キー文字列が分散しやすく、どの値がひとまとまりなのかも見えにくくなります。

そんなときに使いやすいのが @ConfigurationProperties です。

この記事では、Spring Boot で @ConfigurationProperties を使って設定を読み込む最小構成と、実務でよく一緒に入れる validation まで整理します。

検証用のサンプルプロジェクトは GitHub の java-snippets リポジトリ に置いてあります。記事中のコードとあわせて、record ベースの設定バインド、@ConfigurationPropertiesScan@EnableConfigurationProperties、validation の違いをそのまま確認できます。サンプルは 2026-06-21 時点で Spring Boot 3.5.15 / 4.1.0 と Java 17 / 21 の組み合わせでローカル確認済みで、GitHub Actions では Java 17 / 21 / 25 を対象にしています。

先に結論

  • 関連する設定をまとめて扱うなら、@Value より @ConfigurationProperties のほうが見通しがよいです
  • 通常のアプリでは @ConfigurationPropertiesScan を使う最小構成から始めるのが分かりやすいです
  • spring-boot-configuration-processor を入れておくと、IDE 補完や metadata 生成の恩恵を受けやすくなります

@Value ではなく @ConfigurationProperties を使いたくなる場面

たとえば、記事のおすすめ表示に使う設定を考えます。

  • 接続先の URL
  • 1ページあたりの件数
  • タイムアウト
  • キャッシュ設定
  • リトライ設定

これを @Value で読むと、次のように値が分散しやすくなります。

@Service
public class RecommendationClient {

    private final URI endpoint;
    private final int pageSize;
    private final Duration timeout;

    public RecommendationClient(
            @Value("${article.recommendation.endpoint}") URI endpoint,
            @Value("${article.recommendation.page-size}") int pageSize,
            @Value("${article.recommendation.timeout}") Duration timeout) {
        this.endpoint = endpoint;
        this.pageSize = pageSize;
        this.timeout = timeout;
    }
}

これでも動きます。ただ、設定が増えるほど「この設定群は recommendation 用」というまとまりがコード上で見えにくくなります。

@ConfigurationProperties なら、prefix 単位で object にまとめられます。

@ConfigurationProperties("article.recommendation")
public record RecommendationProperties(
        URI endpoint,
        int pageSize,
        Duration timeout,
        Cache cache,
        Retry retry) {

    public record Cache(boolean enabled, Duration ttl, int maxEntries) {
    }

    public record Retry(int maxAttempts, Duration initialInterval) {
    }
}

この形にすると、設定のまとまりがそのまま型として表現されます。

Spring Boot の公式ドキュメントでも、@ConfigurationProperties は型安全な設定プロパティ向けの仕組みとして位置づけられていて、@Value と比べて relaxed binding や metadata support に強みがあります。

最小構成で設定を読み込む

まずは設定ファイルです。サンプルも application.yaml を使っています。

article:
  recommendation:
    endpoint: https://example.internal/recommendations
    page-size: 20
    timeout: 750ms
    cache:
      enabled: true
      ttl: 5m
      max-entries: 1000
    retry:
      max-attempts: 3
      initial-interval: 200ms

次に @ConfigurationProperties を付けた record を定義します。まずは validation なしの最小形です。

package com.github.massakai.snippets.configurationproperties;

import java.net.URI;
import java.time.Duration;
import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("article.recommendation")
public record RecommendationProperties(
        URI endpoint,
        int pageSize,
        Duration timeout,
        Cache cache,
        Retry retry) {

    public record Cache(
            boolean enabled,
            Duration ttl,
            int maxEntries) {
    }

    public record Retry(
            int maxAttempts,
            Duration initialInterval) {
    }
}

利用側は普通の bean と同じように受け取れます。

@Service
public class RecommendationClient {

    private final RecommendationProperties properties;

    public RecommendationClient(RecommendationProperties properties) {
        this.properties = properties;
    }

    public URI endpoint() {
        return properties.endpoint();
    }
}

そして、@ConfigurationProperties を有効化します。

package com.github.massakai.snippets.configurationproperties;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;

@SpringBootApplication
@ConfigurationPropertiesScan
public class ConfigurationPropertiesSampleApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConfigurationPropertiesSampleApplication.class, args);
    }
}

これがいちばん素直な最小構成です。今回の java-snippets サンプルはこの形に validation も加えた版ですが、まずはここまでで「どう bind するか」を押さえると流れを追いやすいです。

必須設定にしたいならバリデーションも付けられる

@ConfigurationProperties は「読める」だけでなく、「この値は必須」「空文字は困る」といった制約も付けられます。

Spring Boot の公式ドキュメントでも、@ConfigurationProperties@Validated を付けて、jakarta.validation の制約アノテーションを使う形が案内されています。

たとえば、さきほどの RecommendationProperties に対して、接続先 URL は必須、リトライ回数は 1 回以上、という制約を足すなら次のように書けます。

package com.github.massakai.snippets.configurationproperties;

import jakarta.validation.Valid;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import java.net.URI;
import java.time.Duration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;

@Validated
@ConfigurationProperties("article.recommendation")
public record RecommendationProperties(
        @NotNull URI endpoint,
        @Min(1) int pageSize,
        @NotNull Duration timeout,
        @NotNull @Valid Cache cache,
        @NotNull @Valid Retry retry) {

    public record Cache(
            boolean enabled,
            @NotNull Duration ttl,
            @Min(1) int maxEntries) {
    }

    public record Retry(
            @Min(1) int maxAttempts,
            @NotNull Duration initialInterval) {
    }
}

ネストした設定にも制約を伝播させたいときは、親側で @Valid を付けます。ネスト object 自体を必須にしたいなら、サンプルのように @NotNull も併記しておくと意図がはっきりします。

この状態で設定が不足していると、起動時に bind と validation が走り、問題のある値を早めに見つけられます。

たとえば endpoint を書き忘れたり、page-size: 0 のような値を入れたりしたときに、アプリを起動してから妙な動きを見るより前に止められます。

java-snippets 側のサンプルでは、この failure もテストしています。validation 違反時には ConfigurationPropertiesBindException の原因として BindValidationException が見え、対象 field と制約コードを追えます。メッセージ本文は実行ロケールで揺れるので、サンプルのテストでは文言そのものではなく field 名と constraint code を見ています。

使うには Bean Validation 実装が classpath に必要です。Spring Boot の validation ドキュメントでは、通常は spring-boot-starter-validation により用意されると説明されています。

Gradle なら、たとえば次のように足せます。

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-validation'
}

今回の java-snippets サンプルは validation ありに更新されています。記事では最小構成の理解を優先しつつ、「実務ではここまで入れることが多い」という位置づけで読むと流れを追いやすいと思います。

@ConfigurationPropertiesScan と @EnableConfigurationProperties の違い

通常のアプリでは、まず @ConfigurationPropertiesScan を使えば十分です。

@ConfigurationPropertiesScan は、指定したパッケージ、未指定なら付与したクラスのパッケージを起点に @ConfigurationProperties を探します。

そのため、今回のように @SpringBootApplication があるパッケージ配下に properties class を置く構成なら、そのまま使えます。

一方で、明示的に登録したい場合は @EnableConfigurationProperties も使えます。

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(RecommendationProperties.class)
class PropertiesConfig {
}

こちらは「この properties class を使う」と明示したいときに便利です。

たとえば、auto-configuration 側やテスト用の小さな設定クラスではこちらのほうが分かりやすいことがあります。

なお、@ConfigurationPropertiesScan には一つ注意点があります。公式 API ドキュメントでは、@Component が付いたクラスは scan では拾われないと説明されています。

そのため、@ConfigurationProperties@Component をなんとなく一緒に付けて、さらに scan にも任せる、という形は避けたほうが分かりやすいです。

record と相性がよい

今の Spring Boot では、record を使った @ConfigurationProperties がかなり書きやすくなっています。

今回のサンプルでも、次のような点が自然に表現できます。

  • URI
  • Duration
  • int
  • boolean
  • ネストした設定 object
  • validation アノテーション

たとえば page-sizepageSize に、initial-intervalinitialInterval に bind されます。これは Spring Boot の relaxed binding によるものです。

record にしておくと、どの設定値が必要なのかがコンストラクタ引数の形で見えるので、設定クラスとしての意図も伝わりやすいです。

primitive の int は未設定時に 0 になるので、サンプルのように必須かつ正の値として扱いたいなら @Min(1) を付けるのが分かりやすいです。未設定と 0 を区別したいなら Integer にする選択もあります。

spring-boot-configuration-processor は入れたほうがよいか

結論からいうと、入れておくほうが扱いやすいです。

これは実行時のバインドに必須というより、コンパイル時に configuration metadata を生成するための依存関係です。

Gradle なら、たとえば次のように入れます。

dependencies {
    annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
    implementation 'org.springframework.boot:spring-boot-starter'
    implementation 'org.springframework.boot:spring-boot-starter-validation'
}

これを入れておくと、IDE 補完や metadata 生成の恩恵を受けやすくなります。

記事を書く側としても、「読者が手元で触ったときに設定名の補完が効きやすい」というのは地味に大きいです。

サンプルでは compileJava 後に metadata が生成されることも確認しています。

@Value のままでよいケース

ここまで書くと、全部 @ConfigurationProperties に寄せたくなるかもしれません。

ただ、単発の値を一つ読むだけなら @Value のほうが短くて分かりやすいこともあります。

たとえば次のようなケースです。

@Service
public class AppNamePresenter {

    private final String appName;

    public AppNamePresenter(@Value("${app.name}") String appName) {
        this.appName = appName;
    }
}

このくらいなら、無理に properties class を作らなくてもよいと思います。ここでも、通常のアプリコードならフィールドインジェクションよりコンストラクタインジェクションのほうが扱いやすいです。

逆に、次のどれかに当てはまるなら @ConfigurationProperties を考えたくなります。

  • 同じ prefix の設定が複数ある
  • ネストした設定がある
  • DurationURI など型付きで受けたい
  • 設定のまとまりを class として表現したい
  • 必須チェックや範囲チェックを起動時にかけたい

まとめ

@ConfigurationProperties は、関連する設定をまとめて型安全に扱いたいときに使いやすい仕組みです。

通常のアプリなら、record@ConfigurationPropertiesScan を使った最小構成から始めるのが分かりやすいです。

そのうえで、IDE 補完や metadata 生成のために spring-boot-configuration-processor も足しておくと、実務ではかなり扱いやすくなります。

@Value を完全に置き換えるというより、単発なら @Value、まとまりなら @ConfigurationProperties と考えると整理しやすそうです。

参考

コメントする

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)