設定管理
設定管理は開発と本番の橋渡しです。1つのコードベース, 複数の設定, 環境の切り替えは1行のパラメータで済みます。
1. 学ぶ内容
- Profileメカニズム:
application-dev.yml/application-prod.ymlによるマルチ環境切り替え - タイプセーフバインディング
@ConfigurationPropertiesとインジェクション@Valueの比較 - 設定優先度の順序:コマンドライン引数 > 環境変数 > 設定ファイル > デフォルト値
- ネストされた設定とList/Map型へのバインディング
- OrderFlowのデータソースとサードパーティAPIキー設定の外部化のベストプラクティス
2. 運用エンジニアの実話
(1) ペインポイント:設定があちこちに散在
BobはOrderFlowの運用エンジニアで, デプロイのたびに「発掘作業」のような思いをしていました。データベースのパスワードがコードにハードコードされ, テスト環境と本番環境の設定が1つのファイルに混在しています。誰かが本番データベースのパスワードを変更したがコードの更新を忘れ, システムが2時間ダウンしました。CharlieにSLAについて問い詰められたとき, Bobは「設定管理がめちゃくちゃなんです」とため息交じりに説明するしかありませんでした。
(2) Spring Boot Profilesによる解決策
Spring BootはProfileメカニズムを使って複数環境の設定を分離します。
# application-dev.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/orderflow_dev
username: dev_user
password: dev_pass
# application-prod.yml
spring:
datasource:
url: jdbc:mysql://prod-db.internal:3306/orderflow
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
(3) 成果
BobがProfileと環境変数を使ってリファクタリングした後, 開発環境はdevを, 本番環境はprodを使用するようになりました。機密情報はコードリポジトリに含まれなくなり, デプロイの切り替えは--spring.profiles.active=prodだけで済み, 設定エラーによるダウンタイムはゼロになりました。
3. Profile:マルチ環境設定
(1) Profileファイルの命名規則
Spring Bootはapplication-{profile}.ymlの命名規則に従ってProfile設定を読み込みます。
src/main/resources/
├── application.yml # 共通設定 (共有)
├── application-dev.yml # 開発Profile
├── application-prod.yml # 本番Profile
└── application-test.yml # テストProfile
graph TD
A["application.yml<br/>共通設定"] --> B["application-dev.yml<br/>開発環境の上書き"]
A --> C["application-prod.yml<br/>本番環境の上書き"]
A --> D["application-test.yml<br/>テスト環境の上書き"]
B --> E["マージされた設定<br/>Profile=dev"]
C --> F["マージされた設定<br/>Profile=prod"]
| 有効化方法 | コマンド | 優先度 |
|---|---|---|
| 設定ファイル | application.ymlにspring.profiles.active=dev |
最低 |
| 環境変数 | SPRING_PROFILES_ACTIVE=dev |
中 |
| コマンドライン引数 | --spring.profiles.active=dev |
最高 |
(1) ▶ サンプル:Profile
# application.yml (共有)
spring:
application:
name: orderflow-service
profiles:
active: dev
server:
port: 8080
出力:
設定が正常に適用されました
# application-dev.yml
spring:
datasource:
url: jdbc:h2:mem:orderflow_dev
username: sa
password:
jpa:
hibernate:
ddl-auto: create-drop
show-sql: true
logging:
level:
com.orderflow: DEBUG
# application-prod.yml
spring:
datasource:
url: jdbc:mysql://${DB_HOST:localhost}:3306/orderflow
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
jpa:
hibernate:
ddl-auto: validate
show-sql: false
logging:
level:
com.orderflow: WARN
4. @Valueと@ConfigurationPropertiesの比較
(1) 2つのインジェクション方法
(1) ▶ サンプル:@Valueインジェクション
@RestController
public class OrderController {
@Value("${orderflow.max-items-per-order:100}")
private int maxItemsPerOrder;
@Value("${orderflow.default-currency:USD}")
private String defaultCurrency;
@GetMapping("/api/config/check")
public Map<String, Object> checkConfig() {
return Map.of(
"maxItemsPerOrder", maxItemsPerOrder,
"defaultCurrency", defaultCurrency
);
}
}
出力:
// 実行成功
(2) ▶ サンプル:@ConfigurationPropertiesによるタイプセーフバインディング
@ConfigurationProperties(prefix = "orderflow")
public record OrderFlowProperties(
int maxItemsPerOrder,
String defaultCurrency,
Duration orderTimeout,
ShippingConfig shipping
) {
public record ShippingConfig(
boolean freeShippingEnabled,
BigDecimal freeShippingThreshold
) {}
}
// メインクラスまたは設定クラスで有効化
@EnableConfigurationProperties(OrderFlowProperties.class)
出力:
// 実行成功
| 項目 | @Value |
@ConfigurationProperties |
|---|---|---|
| 型安全性 | 弱い (主にString) | 強い (自動型変換) |
| ネストされたオブジェクト | 非サポート | サポート |
| コレクションバインディング | 非サポート | List/Mapをサポート |
| 検証 | なし | @Validatedと組み合わせ |
| IDEサポート | ヒントなし | 自動補完 (メタデータ) |
| 使用場面 | 少数の単純な値 | 構造化されたビジネス設定 |
(3) ▶ サンプル:YAMLとConfigurationPropertiesのマッピング
orderflow:
max-items-per-order: 50
default-currency: USD
order-timeout: 30m
shipping:
free-shipping-enabled: true
free-shipping-threshold: 49.99
出力:
設定が反映されました。
// アクセス例
@Component
public class OrderService {
private final OrderFlowProperties props;
public OrderService(OrderFlowProperties props) {
this.props = props;
}
public boolean isFreeShipping(BigDecimal orderTotal) {
return props.shipping().freeShippingEnabled()
&& orderTotal.compareTo(props.shipping().freeShippingThreshold()) >= 0;
}
}
5. 設定優先度の階層
(1) 高い順の優先度
graph TD
A["1. コマンドライン引数<br/>--server.port=9090"] --> B["2. JNDI属性"]
B --> C["3. Javaシステムプロパティ<br/>-Dserver.port=9090"]
C --> D["4. OS環境変数<br/>SERVER_PORT=9090"]
D --> E["5. application-{profile}.yml<br/>Profile固有"]
E --> F["6. application.yml<br/>デフォルト設定"]
F --> G["7. デフォルト値<br/>コード内のアノテーション"]
| 優先度 | ソース | 例 |
|---|---|---|
| 1 (最高) | コマンドライン引数 | --server.port=9090 |
| 2 | JNDIプロパティ | java:comp/env/... |
| 3 | JVMシステムプロパティ | -Dserver.port=9090 |
| 4 | OS環境変数 | SERVER_PORT=9090 |
| 5 | Profile | application-prod.yml |
| 6 | デフォルト設定ファイル | application.yml |
| 7 (最低) | デフォルト値 | @Value("${x:default}") |
6. ネストされた設定とコレクションバインディング
(1) リストとマップのバインディング
(1) ▶ サンプル:リストとマップの設定
orderflow:
supported-currencies:
- USD
- EUR
- GBP
payment-gateways:
stripe:
api-key: ${STRIPE_API_KEY}
webhook-secret: ${STRIPE_WEBHOOK_SECRET}
paypal:
client-id: ${PAYPAL_CLIENT_ID}
secret: ${PAYPAL_SECRET}
出力:
CI/CDパイプライン設定が読み込まれました
パイプラインステータス:合格
テスト:12合格, 0失敗
@ConfigurationProperties(prefix = "orderflow")
public record OrderFlowProperties(
List<String> supportedCurrencies,
Map<String, GatewayConfig> paymentGateways
) {
public record GatewayConfig(
String apiKey,
String webhookSecret,
String clientId,
String secret
) {}
}
7. 総合サンプル:OrderFlowの完全な設定システム
// OrderFlowProperties.java
package com.orderflow.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.math.BigDecimal;
import java.time.Duration;
import java.util.List;
import java.util.Map;
@ConfigurationProperties(prefix = "orderflow")
public record OrderFlowProperties(
int maxItemsPerOrder,
String defaultCurrency,
Duration orderTimeout,
ShippingConfig shipping,
List<String> supportedCurrencies,
Map<String, GatewayConfig> paymentGateways
) {
public record ShippingConfig(
boolean freeShippingEnabled,
BigDecimal freeShippingThreshold
) {}
public record GatewayConfig(
String apiKey,
String webhookSecret,
String clientId,
String secret
) {}
}
// AppConfig.java
package com.orderflow.config;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties(OrderFlowProperties.class)
public class AppConfig {}
# application.yml
spring:
application:
name: orderflow-service
profiles:
active: dev
orderflow:
max-items-per-order: 50
default-currency: USD
order-timeout: 30m
supported-currencies:
- USD
- EUR
- GBP
shipping:
free-shipping-enabled: true
free-shipping-threshold: 49.99
payment-gateways:
stripe:
api-key: ${STRIPE_API_KEY:dev-key}
webhook-secret: ${STRIPE_WEBHOOK_SECRET:dev-secret}
❓ よくある質問
${ENV_VAR}プレースホルダーを使って環境変数を参照する;2).gitignoreで機密設定ファイルを除外する;3)本番環境ではK8s SecretsやVaultでシークレットを管理する。orderflow.supported-currencies[0]=USD, orderflow.supported-currencies[1]=EUR。リストのインデックスは0から始まります。recordをConfigurationPropertiesとして使用する制限は何ですか?recordは不変であり, 読み取り専用の設定に適しています。Spring Boot 3.xはrecordバインディングをサポートしています。ただし, JSR-380検証のために@Validatedと組み合わせることはできません (recordには引数なしコンストラクタがないため), クラスを使用する必要があります。📖 まとめ
- Profileメカニズムによりマルチ環境の設定分離が可能。
application-{profile}.ymlがデフォルト設定を上書き @ConfigurationPropertiesによるタイプセーフバインディングは@Valueより優れており, ネスト, セット, 検証をサポート- 設定優先度:コマンドライン > 環境変数 > Profileファイル > デフォルトファイル > コードのデフォルト値
- 機密情報には
${ENV_VAR}プレースホルダーを使用し, 設定ファイルにハードコードしない - Spring Boot 3.xは
recordをConfigurationPropertiesとして使用することをサポート
📝 練習問題
-
基本問題 (難易度 ⭐):OrderFlowに2つのProfileを設定してください。1つはdev, 1つはprodです。devはH2インメモリデータベース, prodはMySQLを使用し, コマンドライン引数で切り替えてください。
-
応用問題 (難易度 ⭐⭐):
@ConfigurationPropertiesを使ってPaymentGatewayPropertiesを作成し, StripeとPayPalのAPIキー設定を含めてください。キーの値は環境変数からインジェクションしてください。 -
チャレンジ問題 (難易度 ⭐⭐⭐):カスタム
PropertySourceを実装してリモート設定センターから設定を読み込んでください (モックHTTPエンドポイントを使用可)。Spring BootのEnvironment抽象化の設計意図について考察してください。



