#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);
}
}
Опция: 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…
}
Опция: 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);
}
}