Как зарегистрировать активную конфигурацию в приложении Spring Boot?

Мне бы очень хотелось использовать конфигурацию YAML для Spring Boot, так как я считаю ее вполне читаемой и полезной для того, чтобы иметь один файл, показывающий, какие свойства активны в моих разных профилях. К сожалению, я обнаружил, что параметры настройки в application.yml могут быть довольно хрупкими.

Такие вещи, как использование вкладки вместо пробелов, не приведут к тому, что свойства не будут существовать (без предупреждений, насколько я могу видеть), и слишком часто я обнаруживаю, что мои активные профили не установлены из-за неизвестной проблемы с моим YAML.

Итак, мне было интересно, есть ли какие-либо перехватчики, которые позволят мне получить активные профили и свойства в настоящее время, чтобы я мог их записывать.

Точно так же существует ли способ заставить start-up сбой, если application.yml содержит ошибки? Либо это, либо средство для проверки подлинности YAML, чтобы я мог убить процесс запуска.

Ответ 1

У меня была такая же проблема, и я хотел бы, чтобы был флаг отладки, который бы сообщал системе обработки профилей, чтобы выплеснуть некоторые полезные протоколирования. Один из возможных способов сделать это - зарегистрировать прослушиватель событий для контекста вашего приложения и распечатать профили из среды. Я не пробовал делать это так, поэтому ваш пробег может измениться. Я думаю, может быть, что-то вроде того, что описано здесь:

Как добавить привязку к событию инициализации контекста приложения?

Тогда вы сделаете что-то подобное в своем слушателе:

System.out.println("Active profiles: " + Arrays.toString(ctxt.getEnvironment().getActiveProfiles()));

Возможно, стоит попробовать. Другой способ, которым вы, вероятно, могли бы сделать это, - объявить среду, которую нужно ввести в код, где вам нужно распечатать профили. То есть:.

@Component
public class SomeClass {
  @Autowired
  private Environment env;
  ...
  private void dumpProfiles() {
    // Print whatever needed from env here
  }
}

Ответ 2

Оператор Actuator/env отображает свойства, но не показывает, какое значение свойства действительно активно. Очень часто вы можете переопределить свои свойства приложения с помощью

  • свойства приложения для профиля
  • аргументы командной строки
  • Переменные среды ОС

Таким образом, у вас будет одно и то же свойство и разные значения в нескольких источниках.

Снимок ниже печатает значения активных свойств приложения при запуске:

@Configuration
public class PropertiesLogger {
    private static final Logger log = LoggerFactory.getLogger(PropertiesLogger.class);

    @Autowired
    private AbstractEnvironment environment;

    @PostConstruct
    public void printProperties() {

        log.info("**** APPLICATION PROPERTIES SOURCES ****");

        Set<String> properties = new TreeSet<>();
        for (PropertiesPropertySource p : findPropertiesPropertySources()) {
            log.info(p.toString());
            properties.addAll(Arrays.asList(p.getPropertyNames()));
        }

        log.info("**** APPLICATION PROPERTIES VALUES ****");
        print(properties);

    }

    private List<PropertiesPropertySource> findPropertiesPropertySources() {
        List<PropertiesPropertySource> propertiesPropertySources = new LinkedList<>();
        for (PropertySource<?> propertySource : environment.getPropertySources()) {
            if (propertySource instanceof PropertiesPropertySource) {
                propertiesPropertySources.add((PropertiesPropertySource) propertySource);
            }
        }
        return propertiesPropertySources;
    }

    private void print(Set<String> properties) {
        for (String propertyName : properties) {
            log.info("{}={}", propertyName, environment.getProperty(propertyName));
        }
    }

}

Ответ 3

Если application.yml содержит ошибки, это приведет к сбою при запуске. Я думаю, это зависит от того, что вы подразумеваете под "ошибкой". Конечно, он не сработает, если YAML не сформирован. Также, если вы устанавливаете @ConfigurationProperties, которые помечены как ignoreInvalidFields=true, например, или если вы устанавливаете значение, которое невозможно преобразовать. Это довольно широкий диапазон ошибок.

Активные профили, вероятно, будут регистрироваться при запуске с помощью реализации Environment (но в любом случае вам легко взять это и записать в свой код запуска - toString() of the Environment будет отображать список активные профили, я думаю). Активные профили (и более) также доступны в конечной точке /env, если вы добавляете Actuator.

Ответ 4

Если вы хотите получить активные профили перед инициализацией приложения beans/, единственный способ, которым я нашел, - это зарегистрировать собственный баннер в SpringApplication/Builder.

Ответ 5

В дополнение к другим ответам: ведение активных свойств при обновлении контекста.

Java 8

package mypackage;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

@Slf4j
@Component
public class AppContextEventListener {

    @EventListener
    public void handleContextRefreshed(ContextRefreshedEvent event) {
        printActiveProperties((ConfigurableEnvironment) event.getApplicationContext().getEnvironment());
    }

    private void printActiveProperties(ConfigurableEnvironment env) {

        System.out.println("************************* ACTIVE APP PROPERTIES ******************************");

        List<MapPropertySource> propertySources = new ArrayList<>();

        env.getPropertySources().forEach(it -> {
            if (it instanceof MapPropertySource && it.getName().contains("applicationConfig")) {
                propertySources.add((MapPropertySource) it);
            }
        });

        propertySources.stream()
                .map(propertySource -> propertySource.getSource().keySet())
                .flatMap(Collection::stream)
                .distinct()
                .sorted()
                .forEach(key -> {
                    try {
                        System.out.println(key + "=" + env.getProperty(key));
                    } catch (Exception e) {
                        log.warn("{} -> {}", key, e.getMessage());
                    }
                });
        System.out.println("******************************************************************************");
    }
}

котлинский

package mypackage

import mu.KLogging
import org.springframework.context.event.ContextRefreshedEvent
import org.springframework.context.event.EventListener
import org.springframework.core.env.ConfigurableEnvironment
import org.springframework.core.env.MapPropertySource
import org.springframework.stereotype.Component

@Component
class AppContextEventListener {

    companion object : KLogging()

    @EventListener
    fun handleContextRefreshed(event: ContextRefreshedEvent) {
        printActiveProperties(event.applicationContext.environment as ConfigurableEnvironment)
    }

    fun printActiveProperties(env: ConfigurableEnvironment) {
        println("************************* ACTIVE APP PROPERTIES ******************************")
        env.propertySources
                .filter { it is MapPropertySource }
                .map { it as MapPropertySource }
                .filter { it.name.contains("applicationConfig") }
                .map { it -> it.source.keys }
                .flatMap { it }
                .distinctBy { it }
                .sortedBy { it }
                .forEach { it ->
                    try {
                        println("$it=${env.getProperty(it)}")
                    } catch (e: Exception) {
                        logger.warn("$it -> ${e.message}")
                    }
                }
        println("******************************************************************************")
    }
}

Вывод:

************************* ACTIVE APP PROPERTIES ******************************
server.port=3000
spring.application.name=my-app
...
2017-12-29 13:13:32.843  WARN 36252 --- [           main] m.AppContextEventListener        : spring.boot.admin.client.service-url -> Could not resolve placeholder 'management.address' in value "http://${management.address}:${server.port}"
...
spring.datasource.password=
spring.datasource.url=jdbc:postgresql://localhost/my_db?currentSchema=public
spring.datasource.username=db_user
...
******************************************************************************