簡體   English   中英

基於前綴在Spring中對YAML屬性進行后處理

[英]Post-processing YAML properties in Spring based on prefix

我有一個類似的Spring啟動配置YAML

spring:
  application:
    name: my-app
a: this is literal
b: <<this is external due to special first and last chars>>

我想要做的是添加某種解析器,它將檢測b的值是否為<<X>>形式,並將觸發從外部rest api中檢索該值以在內存中覆蓋該值。在YAML傳遞給在運行時保存配置的bean之前

我嘗試使用EnvironmentPostProcessor失敗了,因為我無法獲得實際屬性值,只有屬性 ,因此我無法對值進行后期處理。

目前適用於我的是@Configuration bean,它包含字段ab ,在setter中實現一些東西來檢測spring試圖設置的值是否以<<開頭並以>>結尾>>如果是,則覆蓋什么使用我從其余api檢索的版本加載到pojo中。 這並不理想,因為我最終會有很多重復

在Spring 5中實現這樣的東西的正確方法是什么? 我知道spring屬性支持使用語法${a}對其他屬性的引用,所以必須有一些機制已經允許添加自定義占位符解析器

不知道正確的方法,但從REST調用獲取屬性的一種方法是實現自己的PropertySource ,它獲取(並緩存?)特定命名屬性的值。

這是我使用Spring Boot 2.1.5提出的一個hacky解決方案。 可能更好地使用自定義PropertyResolver

基本上它就像:

  1. 抓住我關心的PropertySource 對於這種情況,它是application.properties 應用程序可以有N個源,因此如果還有其他可以發生<< >>地方,那么您也可以檢查它們。
  2. 循環遍歷<< >>的源值
  3. 如果匹配則動態替換值。

我的房產是:

a=hello from a
b=<<I need special attention>>

我被黑客攻擊的ApplicationListener是:

import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.env.OriginTrackedMapPropertySource;
import org.springframework.context.ApplicationListener;
import org.springframework.core.Ordered;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.web.client.RestTemplate;

import java.util.HashMap;
import java.util.Map;

public class EnvironmentPrepareListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {

    private final RestTemplate restTemplate = new RestTemplate();

    @Override
    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
        // Only focused main application.properties (or yml) configuration
        // Loop through sources to figure out name
        final String propertySourceName = "applicationConfig: [classpath:/application.properties]";
        PropertySource<?> propertySource = event.getEnvironment().getPropertySources()
                .get(propertySourceName);

        Map<String, Object> source = ((OriginTrackedMapPropertySource) propertySource).getSource();
        Map<String, Object> myUpdatedProps = new HashMap<>();
        final String url = "https://jsonplaceholder.typicode.com/todos/1";

        for (Map.Entry<String, Object> entry : source.entrySet()) {
            if (isDynamic(entry.getValue())) {
                String updatedValue = restTemplate.getForEntity(url, String.class).getBody();
                myUpdatedProps.put(entry.getKey(), updatedValue);
            }
        }

        if (!myUpdatedProps.isEmpty()) {
            event.getEnvironment().getPropertySources()
                    .addBefore(
                            propertySourceName,
                            new MapPropertySource("myUpdatedProps", myUpdatedProps)
                    );
        }
    }

    private boolean isDynamic(Object value) {
        return StringUtils.startsWith(value.toString(), "<<")
                && StringUtils.endsWith(value.toString(), ">>");
    }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;
    }
}

擊中/test讓我:

{ "userId": 1, "id": 1, "title": "delectus aut autem", "completed": false }

我最后改變了一些東西來標記特殊屬性。 然后我創建了自己的PropertySource ,就像@Andreas建議的那樣。 它的靈感來自org.springframework.boot.env.RandomValuePropertySource

訣竅是將特殊字符<<>>更改為spring已經使用的語法: ${} ,但就像使用${random.int }的隨機解析器一樣,我做了類似${rest.XXX} 之前我不知道的是,通過這樣做,Spring將第二次使用來自占位符值的新屬性名稱調用所有屬性源(在我之前的示例中為rest.XXX )。 這種方式在屬性源中,如果屬性的名稱以我的前綴rest.開頭,我可以處理該值rest.

這是我的解決方案的簡化版本

public class MyPropertySource extends PropertySource<RestTemplate> {
  private static final String PREFIX = "rest.";

  public MyPropertySource() {
    super(MyPropertySource.class.getSimpleName());
  }

  @Override
  public Object getProperty(@Nonnull String name) {
    String result = null;
    if (name.startsWith(PREFIX)) {
        result = getValueFromRest(name.substring(PREFIX.length()));
    }

    return result;
  }
}

最后,要注冊屬性源,我使用了如此處所述EnvironmentPostProcessor 我找不到一種更簡單的方法,不需要維護一個新文件META-INF/spring.factories

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM