Как я могу создать среду загрузки Spring на основе базы данных / источник свойств?

#spring #spring-boot

#spring #spring-boot

Вопрос:

Целью является запуск приложения Spring Boot со средой, содержащей ключи и значения, загруженные и сгенерированные подключением к базе данных (источником данных).

Или, более абстрактно определено: хотя предпочтительнее использовать конфигурацию только по файлам (быстрее, проще, терпимее, …), Иногда вы найдете варианты использования, когда требуется нестатическая конфигурация на основе файлов.


Spring 3.1 представляет Environment , который на самом деле является распознавателем свойств (extends PropertyResolver ) и основан на списке объектов PropertySource . Такой источник является оболочкой / адаптером для свойств (файла или объекта), карты или чего-то еще. Похоже, это действительно способ получить.

 Properties properties = new Properties();
properties.put("mykey", "in-config");
PropertiesPropertySource propertySource = new PropertiesPropertySource("myProperties", properties);
  

Однако это невозможно сделать в классах @Configuration, поскольку оно должно быть доступно для этапа настройки. Подумайте о чем-то вроде

 @Bean public MyService myService() {
  if ("one".equals(env.getProperty("key")) {
    return new OneService();
  } else {
    return new AnotherService();
  }
}

// alternatively via
@Value("${key}")
private String serviceKey;
  

Кроме того, поддерживаются и более поздние версии Spring Condition .

С OneCondition подобным

 public class OneCondition implements Condition {
  @Override
  public boolean matches(final ConditionContext context, final AnnotatedTypeMetadata metadata) {
    return "one".equals(context.getEnvironment().getProperty("key"));
  }
}
  

Это можно использовать как

 @Bean
@Conditional(OneCondition.class)
public MyService myService() {
    return new OneService();
}
  

Мои нерабочие идеи:

Вариант 1: @PropertySource

Соответствующий процессор аннотаций обрабатывает только файлы. Это нормально, но не для этого варианта использования.

Вариант 2: PropertySourcesPlaceholderConfigurer

Примером с пользовательским источником свойств может быть

 @Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() throws IOException {
  PropertySourcesPlaceholderConfigurer pspc = new PropertySourcesPlaceholderConfigurer();
  pspc.setIgnoreUnresolvablePlaceholders(Boolean.TRUE);

  // create a custom property source and apply into pspc
  MutablePropertySources propertySources = new MutablePropertySources();
  Properties properties = new Properties();
  properties.put("key", "myvalue");
  final PropertiesPropertySource propertySource = new PropertiesPropertySource("pspc", properties);
  propertySources.addFirst(propertySource);
  pspc.setPropertySources(propertySources);

  pspc.setLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:application.properties"));
    return pspc;
}
  

Однако это настраивает только заполнители (т. Е. @Value . Any environment.getProperty() не принесет пользы.

Это более или менее то же самое, что и вариант 1 (меньше магии, больше опций).


Знаете ли вы лучший вариант? В идеале решение должно использовать контекстный источник данных. Однако это концептуально является проблемой, поскольку создание компонента datasource зависит от самих свойств…

Комментарии:

1. Просто используйте an ApplicationContextInitializer для загрузки свойств, создайте PropertySource из него a и добавьте его в Environment . Если вы хотите refresh такого поведения, spring-cloud-config вам понадобится гораздо больше инфраструктуры.

2. Спасибо. Но я должен добавить: если вы хотите использовать SpringApplication , вам spring.factories также нужно будет зарегистрировать этот инициализатор. Ключ есть org.springframework.context.ApplicationContextInitializer .

Ответ №1:

Spring Boot предоставляет несколько разных точек расширения для этого раннего этапа обработки: http://docs.spring.io/spring-boot/docs/current/reference/html/howto-spring-boot-application.html#howto-customize-the-environment-or-application-context

Внутренне эти параметры реализуются с помощью реализаций стандартного Spring ApplicationContextInitializer .

В зависимости от приоритета источника ключ / значение будут доступны как в environment.getProperty() , так и в заполнителях свойств.

Поскольку это прослушиватели контекста предварительной настройки, никакие другие компоненты не доступны, например DataSource . Итак, если свойства должны быть прочитаны из базы данных, источник данных и соединение должны быть созданы вручную (в конечном итоге отдельный поиск соединения с источником данных).


Опция: ApplicationListener для ApplicationEnvironmentPreparedEvent

Создайте реализацию прослушивателя приложений, использующего ApplicationEnvironmentPreparedEvent s и

зарегистрируйте его META-INF/spring.factories и ключ org.springframework.context.ApplicationListener

— или —

используйте SpringApplicationBuilder :

 new SpringApplicationBuilder(App.class)
        .listeners(new MyListener())
        .run(args);
  

Пример

 public class MyListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
  @Override
  public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
    final ConfigurableEnvironment env = event.getEnvironment();
    final Properties props = loadPropertiesFromDatabaseOrSo();
    final PropertiesPropertySource source = new PropertiesPropertySource("myProps", props);
    environment.getPropertySources().addFirst(source);
  }
}
  

Ссылка: http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-spring-application.html#boot-features-application-events-and-listeners


Опция: SpringApplicationRunListener

Помимо специального события, существует также более общий прослушиватель событий, содержащий перехваты для нескольких типов событий.

Создайте реализацию SpringApplicationRunListener и зарегистрируйте ее в META-INF/spring.factories и ключ org.springframework.boot.SpringApplicationRunListener .

Пример

 public class MyAppRunListener implements SpringApplicationRunListener {

  // this constructor is required!
  public MyAppRunListener(SpringApplication application, String... args) {}

  @Override
  public void environmentPrepared(final ConfigurableEnvironment environment) {

    MutablePropertySources propertySources = environment.getPropertySources();

    Properties props = loadPropertiesFromDatabaseOrSo();
    PropertiesPropertySource propertySource = new PropertiesPropertySource("myProps", props);
    propertySources.addFirst(propertySource);
  }

  // and some empty method stubs of the interface…

}
  

Ссылка: http://docs.spring.io/spring-boot/docs/current/reference/html/howto-spring-boot-application.html#howto-customize-the-environment-or-application-context


Опция: ApplicationContextInitializer

Это старый друг для всех «не загрузочных» разработчиков Spring. Однако сначала он удаляет конфигурацию. SpringApplication

Создайте реализацию ApplicationContextInitializer и

зарегистрируйте его META-INF/spring.factories и ключ org.springframework.context.ApplicationContextInitializer .

— или —

используйте SpringApplicationBuilder :

 new SpringApplicationBuilder(App.class)
        .initializers(new MyContextInitializer())
        .run(args);
  

Пример

 public class MyContextInitializer implements ApplicationContextInitializer {
  @Override
  public void initialize(final ConfigurableApplicationContext context) {
    ConfigurableEnvironment environment = context.getEnvironment();

    MutablePropertySources propertySources = environment.getPropertySources();

    Properties props = loadPropertiesFromDatabaseOrSo();
    PropertiesPropertySource propertySource = new PropertiesPropertySource("myProps", props);
    propertySources.addFirst(propertySource);
  }

}