Spring Boot: попытка переопределить application.properties с помощью EnvironmentPostProcessor - PullRequest
2 голосов
/ 07 ноября 2019

Поэтому я пытаюсь использовать ключ / значения, хранящиеся в Cosul, для переопределения значений в application.properties. Я попробовал две вещи.

1) Использование Spring Cloud Consul Config. https://cloud.spring.io/spring-cloud-consul/reference/html/#spring-cloud-consul-config

Это сработало, если у меня не было того же ключа, определенного в моем application.properties. Если это было определено в application.properties, значение в файле свойств использовалось во всех разрешениях аннотации @Value. Это противоположно тому, что я хотел.

2) Поскольку вышеприведенное не сработало, я продолжил создавать собственный EnvironmentPostProcessor. Сначала я попытался создать MapPropertySource и использовал environment.getPropertySources (). AddAfter (..). Это был тот же результат, что и выше. Затем я попытался перебрать все источники свойств, нашел источник с именем, содержащим «applicationConfig: [classpath: / application», и либо установить значение свойства, если оно существует, либо задать новое значение свойства. Кроме того, я добавил MapPropertySource в тот же EnumerableCompositePropertySource, в котором находится источник свойства applicationConfig: [classpath: / application ».

При любом подходе результат всегда один и тот же. Если ключ существует в application.properties, используется это значение.

Что дает? Я буквально переопределяю значение в источниках свойств и вижу значения в отладчике до того, как PostProcessor завершит свою работу. Как значение application.properties все еще попадает в аннотации @Value?

Вот мой текущий PostProcessor.

@Order(Ordered.LOWEST_PRECEDENCE)
public class ConsulPropertyPostProcessor implements EnvironmentPostProcessor {


    private static final String PROPERTY_SOURCE_NAME = "applicationConfigurationProperties";

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        PropertySource<?> system = environment.getPropertySources().get(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME);

        ConsulKVService consulKVService = new ConsulKVServiceImpl().instantiateConsulKVServiceImpl((String)system.getProperty("CONSUL_HOST"), (String)system.getProperty("CONSUL_TOKEN"));
        Map<String, Object> map = consulKVService.getConsulKeysAndValuesByPrefix((String)system.getProperty("CONSUL_PREFIX"));


        addOrReplace(environment.getPropertySources(), map);
    }

    private void addOrReplace(MutablePropertySources propertySources, Map<String, Object> map) {
        MapPropertySource target = new MapPropertySource("applicationConfig: [consulKVs]", map);
        if (propertySources.contains(PROPERTY_SOURCE_NAME)) {
            PropertySource<?> applicationConfigurationPropertySources = propertySources.get(PROPERTY_SOURCE_NAME);

            for(EnumerableCompositePropertySource applicationPropertySource : (ArrayList<EnumerableCompositePropertySource>)applicationConfigurationPropertySources.getSource()){
                if(applicationPropertySource.getName() != null
                        && applicationPropertySource.getName().contains("applicationConfig: [profile=")) {

                    for(PropertySource singleApplicationPropertySource : applicationPropertySource.getSource()){
                        if(singleApplicationPropertySource.getName().contains("applicationConfig: [classpath:/application")){

                            for (String key : map.keySet()) {
                                if(map.get(key) != null) {
                                    if (singleApplicationPropertySource.containsProperty(key)) {
                                        ((Properties) singleApplicationPropertySource.getSource())
                                                .setProperty(key, (String) map.get(key));
                                    } else {
                                        ((Properties) singleApplicationPropertySource.getSource()).put(key, (String) map.get(key));
                                    }
                                }
                            }
                            break;
                        }
                    }

                    applicationPropertySource.add(target);

                    break;


                }
            }

        }

    }
}

Заранее спасибо всем.

РЕДАКТИРОВАТЬ: Пробовалпереопределение метода onApplicationEvent класса ApplicationListener с тем же результатом, что и выше. Вот этот код.

@Log4j
public class ConsulProperties implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {

    static ConfigurableEnvironment configurableEnvironment;
    private static final String PROPERTY_SOURCE_NAME = "applicationConfigurationProperties";

    public static ConfigurableEnvironment getConfigurableEnvironment() {
        return configurableEnvironment;
    }

    @Override
    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
        log.info("Received ApplicationEnvironmentPreparedEvent...");
        ConfigurableEnvironment environment = event.getEnvironment();
        configurableEnvironment = environment;
        Properties props = new Properties();

        ConsulKVService consulKVService = new ConsulKVServiceImpl()
                .instantiateConsulKVServiceImpl((String) configurableEnvironment.getProperty("CONSUL_HOST"),
                        (String) configurableEnvironment.getProperty("CONSUL_TOKEN"));
        Map<String, Object> map = consulKVService.getConsulKeysAndValuesByPrefix((String) configurableEnvironment.getProperty("CONSUL_PREFIX"));
        while(map.values().remove(null));
        addOrReplace(environment.getPropertySources(), map);
    }


    private void addOrReplace(MutablePropertySources propertySources, Map<String, Object> map) {
        MapPropertySource target = new MapPropertySource("applicationConfig: [consulKVs]", map);
        if (propertySources.contains(PROPERTY_SOURCE_NAME)) {
            PropertySource<?> applicationConfigurationPropertySources = propertySources.get(PROPERTY_SOURCE_NAME);

            for(EnumerableCompositePropertySource applicationPropertySource : (ArrayList<EnumerableCompositePropertySource>)applicationConfigurationPropertySources.getSource()){
                if(applicationPropertySource.getName() != null
                        && applicationPropertySource.getName().contains("applicationConfig: [profile=")) {

                    for(PropertySource singleApplicationPropertySource : applicationPropertySource.getSource()){
                        if(singleApplicationPropertySource.getName().contains("applicationConfig: [classpath:/application")){

                            for (String key : map.keySet()) {
                                if (singleApplicationPropertySource.containsProperty(key)) {
                                    ((Properties) singleApplicationPropertySource.getSource())
                                            .setProperty(key, (String) map.get(key));
                                } else {
                                    ((Properties) singleApplicationPropertySource.getSource()).put(key,
                                            map.get(key));
                                }
                            }


                            applicationPropertySource.add(target);

                            Properties properties = new Properties();
                            properties.putAll(map);
                            propertySources.addLast(new PropertiesPropertySource("consulKVs", properties));

                            break;
                        }
                    }


                    break;


                }
            }
        }
    }
}

Ответы [ 2 ]

0 голосов
/ 08 ноября 2019

Существует проблема с вашим кодом, когда вы добавляете новый источник свойств, используя приведенный ниже код. Обратите внимание, что при вызове addLast источник свойств, добавленный с помощью этого метода, имеет наименьший приоритет и никогда не будет обновлять уже доступные свойства.

propertySources.addLast(new PropertiesPropertySource("consulKVs", properties));

Вместо вышеупомянутого вы можете использовать «addFirst» для добавления источника свойства, который должен иметь наивысший приоритет, как указано в приведенном ниже коде. Также доступны некоторые другие методы, такие как «addAfter» и «addBefore», которые вы можете изучить, чтобы добавить источник свойства в точное местоположение. В любом случае «addFirst» будет иметь приоритет над всеми остальными способами, поэтому я думаю, что вы можете использовать «addFirst» для обновления источника свойства.

propertySources.addFirst(new PropertiesPropertySource("consulKVs", properties));

Я протестировал этот сценарий с помощью ApplicationEnvironmentPreparedEvent, и он работает нормально. Надеюсь, это решит вашу проблему.

0 голосов
/ 07 ноября 2019

Похоже, вы пытаетесь изменить соглашение, для которого пружина не предназначена. Код, который вы предоставляете, не так прост в обслуживании и требует глубоких знаний внутренних ресурсов Spring. Честно говоря, я не могу сказать без отладки, как добиться того, чего вы хотите, однако у меня есть другой подход:

Вы можете использовать пружинные профили следующим образом:

Скажем, у вас естьсвойство db.name=abc в application.properties и db.name=xyz в консуле, и я предполагаю, что ваша цель состоит в том, чтобы разрешить db.name=xyz к весне.

В этом случае переместите db.name=abc в application-local.properties и начнитеприложение с --spring.profiles.active=local, если вы хотите иметь свойство из локального файла, и без этого профиля, если вы хотите пойти с консулом.

Вы можете даже динамически добавить активный профиль в EnvironmentPostProcessor (у вас уже есть), но это одна строка кода в EnvironmentPostProcessor.

...