Spring Cloud コンテキスト: アプリケーションコンテキストサービス

Spring Boot は、Spring でアプリケーションを構築する方法について独自の見解を持っています。たとえば、共通の構成ファイル用の従来の場所があり、共通の管理および監視タスク用のエンドポイントがあります。Spring Cloud はそ上に構築され、システム内の多くのコンポーネントが使用する、場合によって必要となるいくつかの機能を追加します。

ブートストラップアプリケーションコンテキスト

Spring Cloud アプリケーションは、メインアプリケーションの親コンテキストである「ブートストラップ」コンテキストを作成することによって動作します。このコンテキストは、外部ソースから構成プロパティをロードし、ローカルの外部構成ファイル内のプロパティを復号化するロールを果たします。2 つのコンテキストは、Spring アプリケーションの外部プロパティのソースである Environment を共有します。デフォルトでは、ブートストラッププロパティ (bootstrap.properties ではなく、ブートストラップフェーズ中に読み込まれるプロパティ) は高い優先順位で追加されるため、ローカル構成でオーバーライドすることはできません。

ブートストラップコンテキストは、メインアプリケーションコンテキストとは異なる外部構成の検索規則を使用します。application.yml (または .properties) の代わりに bootstrap.yml を使用して、ブートストラップとメインコンテキストの外部構成を適切に分離することができます。次のリストは例を示しています。

bootstrap.yml
spring:
  application:
    name: foo
  cloud:
    config:
      uri: ${SPRING_CONFIG_URI:http://localhost:8888}

アプリケーションがサーバーからのアプリケーション固有の構成を必要とする場合は、spring.application.name ( bootstrap.yml または application.yml 内) を設定することをお勧めします。プロパティ spring.application.name をアプリケーションのコンテキスト ID として使用するには、それを bootstrap.[properties | yml] に設定する必要があります。

特定のプロファイル構成を取得する場合は、bootstrap.[properties | yml] で spring.profiles.active も設定する必要があります。

spring.cloud.bootstrap.enabled=false を (たとえば、システムプロパティで) 設定すると、ブートストラッププロセスを完全に無効にすることができます。

アプリケーションコンテキスト階層

SpringApplication または SpringApplicationBuilder からアプリケーションコンテキストを構築すると、ブートストラップコンテキストがそのコンテキストの親として追加されます。子コンテキストが親からプロパティソースとプロファイルを継承するのは Spring の機能であるため、Spring Cloud Config を使用せずに同じコンテキストを構築する場合と比較して、「メイン」アプリケーションコンテキストには追加のプロパティソースが含まれます。追加のプロパティソースは次のとおりです。

  • “ブートストラップ”: ブートストラップコンテキストで PropertySourceLocators が見つかり、空ではないプロパティがある場合、オプションの CompositePropertySource が高い優先度で表示されます。例としては、Spring Cloud Config サーバーからのプロパティがあります。このプロパティソースの内容をカスタマイズする方法については、"ブートストラッププロパティソースのカスタマイズ" を参照してください。

Spring Cloud 2022.0.3 より前は、PropertySourceLocators (Spring Cloud Config 用のものを含む) は、ブートストラップコンテキストではなく、メインアプリケーションコンテキストで実行されていました。bootstrap.[properties | yaml] で spring.cloud.config.initialize-on-context-refresh=true を設定すると、ブートストラップコンテキスト中に PropertySourceLocators を強制的に実行できます。
  • “applicationConfig: [ クラスパス: bootstrap.yml]」 (および関連ファイル (Spring プロファイルがアクティブな場合)): bootstrap.yml (または .properties) がある場合、これらのプロパティはブートストラップコンテキストの構成に使用されます。その後、親が設定されると、子コンテキストに追加されます。これらは、application.yml (または .properties) や、Spring Boot アプリケーション作成プロセスの通常の部分として子に追加されるその他のプロパティソースよりも優先順位が低くなります。これらのプロパティソースの内容をカスタマイズする方法については、"ブートストラッププロパティの場所の変更" を参照してください。

プロパティソースの順序付け規則により、「ブートストラップ」エントリが優先されます。ただし、これらには bootstrap.yml からのデータは含まれていないことに注意してください。bootstrap.yml は優先順位が非常に低いですが、デフォルトの設定に使用できます。

作成した ApplicationContext の親コンテキストを設定することによって、コンテキスト階層を継承できます。たとえば、独自のインターフェースを使用するか、SpringApplicationBuilder の便利なメソッド (parent()child()sibling()) を使用します。ブートストラップコンテキストは、自分で作成した最も古い祖先の親です。階層内のすべてのコンテキストには、値が誤って親から子孫に昇格されることを避けるために、独自の「ブートストラップ」(空の場合もある) プロパティソースがあります。構成サーバーがある場合、階層内のすべてのコンテキストは (原則として) 異なる spring.application.name を持つことができ、異なる リモートプロパティソースを持つこともできます。通常の Spring アプリケーションコンテキストの動作ルールがプロパティの解決に適用されます。子コンテキストのプロパティは、名前およびプロパティソース名によって、親のプロパティをオーバーライドします。(子に親と同じ名前のプロパティソースがある場合、親の値は子に含まれません)。

SpringApplicationBuilder を使用すると、階層全体で Environment を共有できますが、これはデフォルトではないことに注意してください。兄弟コンテキストは (特に)、親と共通の値を共有する場合でも、同じプロファイルやプロパティソースを持つ必要はありません。

ブートストラッププロパティの場所の変更

bootstrap.yml (または .properties) の場所は、たとえばシステムプロパティで spring.cloud.bootstrap.name (デフォルト: bootstrap)、spring.cloud.bootstrap.location (デフォルト: 空)、または spring.cloud.bootstrap.additional-location (デフォルト: 空) を設定することで指定できます。

これらのプロパティは、同じ名前の spring.config.* バリアントと同様に動作します。spring.cloud.bootstrap.location では、デフォルトの場所が置き換えられ、指定された場所のみが使用されます。デフォルトのリストに場所を追加するには、spring.cloud.bootstrap.additional-location を使用できます。実際、これらのプロパティは、Environment にこれらのプロパティを設定することで、ブートストラップ ApplicationContext をセットアップするために使用されます。アクティブなプロファイルがある場合 (spring.profiles.active から、または構築しているコンテキストの Environment API を通じて)、通常の Spring Boot アプリと同様に、そのプロファイル内のプロパティも読み込まれます (たとえば、development プロファイルの場合は bootstrap-development.properties から)。

リモートプロパティの値のオーバーライド

ブートストラップコンテキストによってアプリケーションに追加されるプロパティソースは、多くの場合「リモート」です (たとえば、Spring Cloud Config サーバーから)。デフォルトでは、ローカルでオーバーライドすることはできません。アプリケーションが独自のシステムプロパティまたは構成ファイルで リモートプロパティをオーバーライドできるようにする場合、リモートプロパティソースは spring.cloud.config.allowOverride=true を設定してアクセス許可を付与する必要があります (これをローカルで設定することはできません)。このフラグが設定されると、システムプロパティとアプリケーションのローカル構成に関連した リモートプロパティの場所が、2 つのより詳細な設定によって制御されます。

  • spring.cloud.config.overrideNone=true: ローカルプロパティソースからオーバーライドします。

  • spring.cloud.config.overrideSystemProperties=false: システムプロパティ、コマンドライン引数、環境変数のみ (ローカル構成ファイルは不可) が リモート設定をオーバーライドする必要があります。

ブートストラップ構成のカスタマイズ

ブートストラップコンテキストは、org.springframework.cloud.bootstrap.BootstrapConfiguration という名前のキーの /META-INF/spring.factories にエントリを追加することで、任意の操作を行うように設定できます。これには、コンテキストの作成に使用される Spring @Configuration クラスのカンマ区切りのリストが保持されます。オートワイヤーのためにメインアプリケーションコンテキストで使用できるようにする Bean は、ここで作成できます。ApplicationContextInitializer 型の @Beans には特約がございます。起動シーケンスを制御したい場合は、クラスに @Order アノテーションをマークできます (デフォルトの順序は last)。

カスタム BootstrapConfiguration を追加するときは、追加するクラスが必要のない「メイン」アプリケーションコンテキストに誤って @ComponentScanned にならないように注意してください。Boot 構成クラスには別のパッケージ名を使用し、その名前が @ComponentScan または @SpringBootApplication アノテーション付き構成クラスですでにカバーされていないことを確認してください。

ブートストラッププロセスは、メイン SpringApplication インスタンスに初期化子を挿入して終了します (これは、スタンドアロンアプリケーションとして実行されるか、アプリケーションサーバーにデプロイされるかに関係なく、通常の Spring Boot 起動シーケンスです)。まず、spring.factories にあるクラスからブートストラップコンテキストが作成されます。次に、型 ApplicationContextInitializer のすべての @Beans が、メイン SpringApplication が開始される前にメイン SpringApplication に追加されます。

ブートストラッププロパティソースのカスタマイズ

ブートストラッププロセスによって追加される外部構成のデフォルトのプロパティソースは Spring Cloud Config サーバーですが、型 PropertySourceLocator の Bean を ( spring.factories を介して) ブートストラップコンテキストに追加することで、追加のソースを追加できます。たとえば、別のサーバーまたはデータベースから追加のプロパティを挿入できます。

例として、次のカスタムロケーターを考えてみましょう。

@Configuration
public class CustomPropertySourceLocator implements PropertySourceLocator {

    @Override
    public PropertySource<?> locate(Environment environment) {
        return new MapPropertySource("customProperty",
                Collections.<String, Object>singletonMap("property.from.sample.custom.source", "worked as intended"));
    }

}

渡される Environment は、これから作成される ApplicationContext 用の Environment です。つまり、追加のプロパティソースを提供する Environment です。通常の Spring Boot が提供するプロパティソースがすでに存在するため、使用して、この Environment に固有のプロパティソースを見つけることができます (たとえば、デフォルトの Spring Cloud Config サーバープロパティソースロケーターで行われるように、spring.application.name にキーを入力することによって)。

このクラスを含む jar を作成し、次の設定を含む META-INF/spring.factories を追加すると、その jar をクラスパスに含むアプリケーションに customProperty PropertySource が表示されます。

org.springframework.cloud.bootstrap.BootstrapConfiguration=sample.custom.CustomPropertySourceLocator

Spring Cloud 2022.0.3 以降、Spring Cloud は PropertySourceLocators を 2 回呼び出すようになります。最初のフェッチでは、プロファイルを含まないプロパティソースが取得されます。これらのプロパティソースには、spring.profiles.active を使用してプロファイルをアクティブ化する機会があります。メインアプリケーションコンテキストが開始された後、PropertySourceLocators が 2 回目に呼び出されます。このときはアクティブなプロファイルが使用され、PropertySourceLocators はプロファイルを持つ追加の PropertySources を見つけることができます。

ロギング構成

Spring Boot を使用してログ設定を構成する場合、すべてのイベントに適用したい場合は、この構成を bootstrap.[yml | properties] に配置する必要があります。

Spring Cloud がログ構成を適切に初期化するには、カスタムプレフィックスを使用することはできません。例: custom.loggin.logpath の使用は、ロギングシステムの初期化時に Spring Cloud によって認識されません。

環境の変化

アプリケーションは EnvironmentChangeEvent をリッスンし、いくつかの標準的な方法で変更に反応します (追加の ApplicationListeners は通常の方法で @Beans として追加できます)。EnvironmentChangeEvent が観察されると、変更されたキー値のリストがあり、アプリケーションはそれらを使用して次のことを行います。

  • コンテキスト内の @ConfigurationProperties Bean を再バインドします。

  • logging.level.* のプロパティのロガーレベルを設定します。

Spring Cloud Config クライアントは、デフォルトでは Environment の変更をポーリングしないことに注意してください。一般に、変更を検出するためにそのアプローチはお勧めしません (ただし、@Scheduled アノテーションを使用してセットアップすることはできます)。スケールアウトされたクライアントアプリケーションがある場合は、(たとえば、Spring Cloud Bus [GitHub] (英語) を使用して) 変更をポーリングさせる代わりに、すべてのインスタンスに EnvironmentChangeEvent をブロードキャストすることをお勧めします。

EnvironmentChangeEvent は、実際に Environment に変更を加えてイベントを発行できる限り、大規模なクラスのリフレッシュユースケースをカバーします。これらの API はパブリックであり、コア Spring の一部であることに注意してください。/configprops エンドポイント (標準の Spring Boot Actuator 機能) にアクセスすると、変更が @ConfigurationProperties Bean にバインドされていることを確認できます。たとえば、DataSource は実行時に maxPoolSize を変更し (Spring Boot によって作成されるデフォルトの DataSource は @ConfigurationProperties Bean です)、容量を動的に拡張できます。@ConfigurationProperties の再バインドは、リフレッシュをより詳細に制御する必要がある場合や、ApplicationContext 全体に対してアトミックな変更が必要な場合など、別の大きなクラスのユースケースには対応していません。これらの関心事に対処するために、@RefreshScope があります。

@ConfigurationProperties でアノテーションが付けられた Java レコードはリフレッシュできません。

リフレッシュスコープ

構成の変更がある場合、@RefreshScope としてマークされている Spring @Bean は特別な扱いを受けます。この機能は、初期化時にのみ構成が挿入されるステートフル Bean の問題に対処します。たとえば、データベース URL が Environment を通じて変更されたときに DataSource が開いている接続がある場合、おそらく、それらの接続の所有者が実行中の作業を完了できるようにする必要があります。その後、次回何かがプールから接続を借用するときに、新しい URL を持つ接続を取得します。

場合によっては、一度しか初期化できない一部の Bean に @RefreshScope アノテーションを適用することが必須になる場合もあります。Bean が「不変」の場合、Bean に @RefreshScope のアノテーションを付けるか、プロパティキー spring.cloud.refresh.extra-refreshable にクラス名を指定する必要があります。

DataSource Bean が HikariDataSource である場合は、リフレッシュできません。これは spring.cloud.refresh.never-refreshable のデフォルト値です。リフレッシュする必要がある場合は、別の DataSource 実装を選択してください。

リフレッシュスコープ Bean は、使用時 (つまり、メソッドの呼び出し時) に初期化される遅延プロキシであり、スコープは初期化された値のキャッシュとして機能します。次のメソッド呼び出しで Bean を強制的に再初期化するには、そのキャッシュエントリを無効にする必要があります。

RefreshScope はコンテキスト内では Bean であり、ターゲットキャッシュをクリアすることでスコープ内のすべての Bean をリフレッシュするパブリック refreshAll() メソッドを備えています。/refresh エンドポイントは、この機能を (HTTP または JMX 経由で) 公開します。個々の Bean を名前でリフレッシュするには、refresh(String) メソッドもあります。

/refresh エンドポイントを公開するには、次の構成をアプリケーションに追加する必要があります。

management:
  endpoints:
    web:
      exposure:
        include: refresh
@RefreshScope は (技術的には) @Configuration クラスで動作しますが、予期しない動作が発生する可能性があります。例: そのクラスで定義されているすべての @Beans 自体が @RefreshScope にあるという意味ではありません。具体的には、これらの Bean に依存するものは、それ自体が @RefreshScope 内にない限り、リフレッシュの開始時にリフレッシュされることに依存できません。その場合、リフレッシュ時に再構築され、その依存関係が再注入されます。その時点で、それらはリフレッシュされた @Configuration から再初期化されます)。
構成値を削除してからリフレッシュを実行しても、構成値の存在はリフレッシュされません。リフレッシュ後に値をリフレッシュするには、構成プロパティが存在する必要があります。アプリケーション内の値の存在に依存している場合は、代わりに値の不在に依存するようにロジックを切り替えることができます。もう 1 つのオプションは、アプリケーションの構成に値が存在しないのではなく、値の変化に依存することです。
コンテキストのリフレッシュは、Spring AOT 変換およびネイティブイメージではサポートされていません。AOT およびネイティブイメージの場合、spring.cloud.refresh.enabled を false に設定する必要があります。

再起動時にスコープをリフレッシュ

再起動時に Bean をシームレスにリフレッシュすることは、JVM Checkpoint Restore ( Project CRaC [GitHub] (英語) など) で実行されるアプリケーションに特に役立ちます。この機能を可能にするために、再起動時にコンテキストリフレッシュをトリガーする RefreshScopeLifecycle Bean をインスタンス化することで、構成プロパティが再バインドされ、@RefreshScope アノテーションが付けられた Bean がリフレッシュされます。spring.cloud.refresh.on-restart.enabled を false に設定することで、この動作を無効にできます。

暗号化と復号化

Spring Cloud には、プロパティ値をローカルで復号化するための Environment プリプロセッサーがあります。Spring Cloud Config サーバーと同じルールに従い、encrypt.* を介した同じ外部構成を持ちます。暗号化された値を {cipher}* の形式で使用でき、有効なキーがある限り、メインアプリケーションコンテキストが Environment 設定を取得する前に値が復号化されます。アプリケーションで暗号化機能を使用するには、クラスパス (Maven 座標: org.springframework.security:spring-security-rsa) に Spring Security RSA を含める必要があります。また、JVM には完全な強度の JCE 拡張機能も必要です。

「不正なキーサイズ」による例外が発生し、Sun の JDK を使用している場合は、Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction ポリシーファイルをインストールする必要があります。詳細については、次のリンクを参照してください。

使用する JRE/JDK x64/x86 のバージョンに応じて、ファイルを JDK/jre/lib/security フォルダーに抽出します。

エンドポイント

Spring Boot Actuator アプリケーションの場合、追加の管理エンドポイントがいくつか利用可能です。次のものが使用できます。

  • POST を /actuator/env に変更して、Environment を更新し、@ConfigurationProperties とログレベルを再バインドします。このエンドポイントを有効にするには、management.endpoint.env.post.enabled=true を設定する必要があります。

  • /actuator/refresh を使用して Boot ストラップコンテキストを再ロードし、@RefreshScope Bean をリフレッシュします。

  • /actuator/restart を使用して ApplicationContext を閉じ、再起動します (デフォルトでは無効)。

  •  Lifecycle メソッド ( ApplicationContextstop() および start() ) を呼び出すための /actuator/pause および /actuator/resume

/actuator/env エンドポイントに対して POST メソッドを有効にすると、アプリケーション環境変数の管理に柔軟性と利便性が提供されますが、潜在的なセキュリティリスクを防ぐために、エンドポイントがセキュリティで保護され、監視されていることを確認することが重要です。spring-boot-starter-security 依存関係を追加して、アクチュエーターのエンドポイントのアクセス制御を構成します。
/actuator/restart エンドポイントを無効にすると、/actuator/pause および /actuator/resume エンドポイントも無効になります。これは、これらは /actuator/restart の特殊なケースにすぎないためです。