Springでのプロパティファイルの再読み込み

1. 概要

このチュートリアルでは、https://www.baeldung.com/properties-with-spring [Springアプリケーションのプロパティ]をリロードする方法を示します。

2. Springでのプロパティの読み取り

Springのプロパティにアクセスするためのさまざまなオプションがあります。
  1. EnvironmentEnvironment_を注入してから使用できます
    _Environment#getProperty_は、特定のプロパティを読み取ります。 _Environment_には、システムプロパティ、
    – D_パラメーター、application.properties(.yml)_などのさまざまなプロパティソースが含まれています。 また、 @ PropertySource_を使用して、_Environment_に追加のプロパティソースを追加できます。

  2. Properties —プロパティファイルを_Properties_にロードできます
    インスタンス、_properties.get(“ property”)._を呼び出してBeanで使用します

  3. @Value —できる
    _ @ Value($ \ {‘property’})_アノテーションを使用して、Beanに特定のプロパティを挿入します。

  4. _https://www.baeldung.com/configuration-properties-in-spring-boot [@ConfigurationProperties] _
    — _ @ ConfigurationProperties_を使用して、Beanの階層プロパティをロードできます。

3. 外部ファイルからプロパティをリロードする

実行中にファイルのプロパティを変更するには、そのファイルをjarの外部のどこかに配置する必要があります。 次に、コマンドライン__ __parameter _–spring.config.location = file:// \ {ファイルへのパス} _を使用して、Springの場所を指定します。 または、_application.properties._に配置できます
ファイルベースのプロパティでは、ファイルをリロードする方法を選択する必要があります。 たとえば、ファイルを読み取り、プロパティを更新するエンドポイントまたはスケジューラを開発できます。
ファイルをリロードする便利なライブラリの1つは、Apache commons-configurationです。 https://commons.apache.org/proper/commons-configuration/apidocs/org/apache/commons/configuration2/PropertiesConfiguration.html[_PropertiesConfiguration_]をさまざまな_ReloadingStrategy_とともに使用できます。
https://search.maven.org/search?q=g:commons-configuration%20a:commons-configuration[_commons-configuration_]を_pom.xml_に追加しましょう。
<dependency>
    <groupId>commons-configuration</groupId>
    <artifactId>commons-configuration</artifactId>
    <version>1.10</version>
</dependency>
次に、後で使用する_PropertiesConfiguration_ Beanを作成するメソッドを追加します。
@Bean
@ConditionalOnProperty(name = "spring.config.location", matchIfMissing = false)
public PropertiesConfiguration propertiesConfiguration(
  @Value("${spring.config.location}") String path) throws Exception {
    String filePath = new File(path.substring("file:".length())).getCanonicalPath();
    PropertiesConfiguration configuration = new PropertiesConfiguration(
      new File(filePath));
    configuration.setReloadingStrategy(new FileChangedReloadingStrategy());
    return configuration;
}
上記のコードでは、_FileChangedReloadingStrategy_をデフォルトの更新遅延を持つ再ロード戦略として設定しています。 これは、_PropertiesConfiguration_がファイルの変更日をチェックすることを意味します*最後のチェックが5000ms前の場合*。
_FileChangedReloadingStrategy#setRefreshDelay._を使用して遅延をカスタマイズできます。

3.1. _Environment_プロパティの再読み込み

_Environment_インスタンスを介して読み込まれたプロパティを再読み込みする場合は、_PropertySource_を拡張し、_PropertiesConfiguration_を使用して外部プロパティファイルから新しい値を返す必要があります*。
_PropertySource_の拡張から始めましょう。
public class ReloadablePropertySource extends PropertySource {

    PropertiesConfiguration propertiesConfiguration;

    public ReloadablePropertySource(String name, PropertiesConfiguration propertiesConfiguration) {
        super(name);
        this.propertiesConfiguration = propertiesConfiguration;
    }

    public ReloadablePropertySource(String name, String path) {
        super(StringUtils.isEmpty(name) ? path : name);
        try {
            this.propertiesConfiguration = new PropertiesConfiguration(path);
            this.propertiesConfiguration.setReloadingStrategy(new FileChangedReloadingStrategy());
        } catch (Exception e) {
            throw new PropertiesException(e);
        }
    }

    @Override
    public Object getProperty(String s) {
        return propertiesConfiguration.getProperty(s);
    }
}
_getProperty_メソッドをオーバーライドして_PropertiesConfiguration#getProperty._に委任しているため、更新の遅延に応じて更新された値を間隔でチェックします。
次に、_ReloadablePropertySource_をEnvironment& ’sプロパティソースに追加します。
@Configuration
public class ReloadablePropertySourceConfig {

    private ConfigurableEnvironment env;

    public ReloadablePropertySourceConfig(@Autowired ConfigurableEnvironment env) {
        this.env = env;
    }

    @Bean
    @ConditionalOnProperty(name = "spring.config.location", matchIfMissing = false)
    public ReloadablePropertySource reloadablePropertySource(PropertiesConfiguration properties) {
        ReloadablePropertySource ret = new ReloadablePropertySource("dynamic", properties);
        MutablePropertySources sources = env.getPropertySources();
        sources.addFirst(ret);
        return ret;
    }
}
*新しいプロパティソースを最初のアイテムとして追加しました*同じキーを持つ既存のプロパティをオーバーライドするためです。
_Environment_からプロパティを読み取るBeanを作成しましょう。
@Component
public class EnvironmentConfigBean {

    private Environment environment;

    public EnvironmentConfigBean(@Autowired Environment environment) {
        this.environment = environment;
    }

    public String getColor() {
        return environment.getProperty("application.theme.color");
    }
}
他の再読み込み可能な外部プロパティソースを追加する必要がある場合、最初にカスタム_PropertySourceFactory_を実装する必要があります。
public class ReloadablePropertySourceFactory extends DefaultPropertySourceFactory {
    @Override
    public PropertySource<?> createPropertySource(String s, EncodedResource encodedResource)
      throws IOException {
        Resource internal = encodedResource.getResource();
        if (internal instanceof FileSystemResource)
            return new ReloadablePropertySource(s, ((FileSystemResource) internal)
              .getPath());
        if (internal instanceof FileUrlResource)
            return new ReloadablePropertySource(s, ((FileUrlResource) internal)
              .getURL()
              .getPath());
        return super.createPropertySource(s, encodedResource);
    }
}
次に、_ @ PropertySource_を使用してコンポーネントのクラスに注釈を付けます。
@PropertySource(value = "file:path-to-config", factory = ReloadablePropertySourceFactory.class)

3.2. プロパティインスタンスの再読み込み

_Environment_は、特にファイルからプロパティをリロードする必要がある場合、_Properties_よりも優れた選択肢です。 ただし、必要な場合は、_java.util.Properties_を拡張できます。
public class ReloadableProperties extends Properties {
    private PropertiesConfiguration propertiesConfiguration;

    public ReloadableProperties(PropertiesConfiguration propertiesConfiguration) throws IOException {
        super.load(new FileReader(propertiesConfiguration.getFile()));
        this.propertiesConfiguration = propertiesConfiguration;
    }

    @Override
    public String getProperty(String key) {
        String val = propertiesConfiguration.getString(key);
        super.setProperty(key, val);
        return val;
    }

    // other overrides
}
_getProperty_とそのオーバーロードをオーバーライドし、_PropertiesConfiguration_インスタンスに委任しました。 これで、このクラスのBeanを作成し、コンポーネントに注入できます。

3.3. _ @ ConfigurationProperties_を使用してBeanをリロードする

_ @ ConfigurationProperties_で同じ効果を得るには、インスタンスを再構築する必要があります。
ただし、Springは_prototype_または_request_ scopeを使用してコンポーネントの新しいインスタンスのみを作成します。
したがって、環境をリロードする手法もそれらに対しては機能しますが、シングルトンの場合、エンドポイントを実装してBeanを破棄および再作成するか、Bean内でプロパティのリロードを処理する以外に選択肢はありません。

3.4. _ @ Value_を使用してBeanをリロードする

_ @ Value_注釈には、link:#reloading-config-prop [_ @ ConfigurationProperties_]と同じ制限があります。

4. アクチュエータとクラウドによるプロパティの再読み込み

link:/spring-boot-actuators[Spring Actuator]は、ヘルス、メトリック、および構成にさまざまなエンドポイントを提供しますが、Beanの更新には何も提供しません。 したがって、_ / refresh_エンドポイントを追加するためにSpring Cloudが必要です。 このエンドポイントは、_Environment_のすべてのプロパティソースをリロードし、https://static.javadoc.io/org.springframework.cloud/spring-cloud-commons-parent/1.1.9.RELEASE/org/springframework/cloud/context/を公開しますenvironment / EnvironmentChangeEvent.html [_EnvironmentChangeEvent _] __.__
Spring Cloudはhttps://static.javadoc.io/org.springframework.cloud/spring-cloud-commons-parent/1.1.4.RELEASE/org/springframework/cloud/context/scope/refresh/RefreshScope.htmlも導入しています。 [_ @ RefreshScope_]。構成クラスまたはBeanに使用できます。 その結果、デフォルトのスコープはlink:/spring-bean-scopes[_singleton_]ではなく_refresh_になります。
_refresh_スコープを使用して、Springは_EnvironmentChangeEvent_でこれらのコンポーネントの内部キャッシュをクリアします。 次に、Beanへの次のアクセスで、新しいインスタンスが作成されます。_+ _
最初に、spring-boot-starter-actuatorをpom.xmlに追加します。
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
次に、https://search.maven.org/search?q = g:org.springframework.cloud%20a:spring-cloud-dependencies [_spring-cloud-dependencies_]もインポートしましょう。
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<properties>
    <spring-cloud.version>Greenwich.SR1</spring-cloud.version>
</properties>
そして、_spring-cloud-starter_を追加します。
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter</artifactId>
</dependency>
最後に、更新エンドポイントを有効にしましょう。
management.endpoints.web.exposure.include=refresh
Spring Cloudを使用する場合、https://www.baeldung.com/spring-cloud-configuration [Config Server]を設定してプロパティを管理できますが、外部ファイルを継続することもできます。 これで、プロパティを読み取る他の2つの方法、_ @ Value_および_ @ ConfigurationProperties_を処理できます。

4.1. _ @ ConfigurationProperties_を使用してBeanを更新する

_ @ RefreshScope_で_ @ ConfigurationProperties_を使用する方法を示しましょう。
@Component
@ConfigurationProperties(prefix = "application.theme")
@RefreshScope
public class ConfigurationPropertiesRefreshConfigBean {
    private String color;

    public void setColor(String color) {
        this.color = color;
    }

    //getter and other stuffs
}
Beanは、ルート_“ application _._ theme” _ property__から“ _color” _プロパティを読み取ります。 __ * Springのドキュメントに従って、setterメソッドが必要であることに注意してください。*
外部設定ファイルの「_ application.theme.color」の値を変更した後、_ / refresh _を呼び出すことができるため、次回アクセス時にBeanから新しい値を取得できます。

4.2. _ @ Value_でBeanを更新する

サンプルコンポーネントを作成しましょう。
@Component
@RefreshScope
public class ValueRefreshConfigBean {
    private String color;

    public ValueRefreshConfigBean(@Value("${application.theme.color}") String color) {
        this.color = color;
    }
    //put getter here
}
リフレッシュのプロセスは上記と同じです。
*ただし、_singleton_ scopeを明示的に指定したBeanでは、_ / refresh_は機能しないことに注意する必要があります。*

5. 結論

このチュートリアルでは、Spring Cloud機能を使用して、または使用せずにプロパティをリロードする方法を示しました。 また、各手法の落とし穴と例外を示しました。
完全なコードはhttps://github.com/eugenp/tutorials/tree/master/spring-boot-properties[GitHubプロジェクト]で入手できます。