如何构建基于数据库的Spring Boot环境/属性源?

2024-01-03

目标是使用包含由数据库连接(数据源)加载和生成的键和值的环境来运行 Spring Boot 应用程序。

或者,更抽象的定义:虽然应该首选仅通过文件进行配置(更快、更容易、更宽容……),但有时您会发现需要基于非静态文件的配置的用例。


Spring 3.1介绍Environment这实际上是一个属性解析器(扩展PropertyResolver)并且基于对象列表PropertySource。这样的源是属性(文件或对象)、映射或其他东西的包装器/适配器。看起来确实是这样的获取方式。

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

但是,这不能在 @Configuration 类中完成,因为它必须可用for配置阶段。想想类似的事情

@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以及。

With a OneCondition like

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;
}

然而,这仅配置占位符 (i.e. @Value. Any environment.getProperty()不会获利。

这或多或少与选项 1 相同(魔法更少,选项更多)。


你知道更好的选择吗?理想情况下,该解决方案将使用上下文数据源。然而,这在概念上是一个问题,因为数据源 bean 创建依赖于属性本身......


Spring Boot 为这个早期处理步骤提供了一些不同的扩展点:http://docs.spring.io/spring-boot/docs/current/reference/html/howto-spring-boot-application.html#howto-customize-the-environment-or-application-context 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()以及属性占位符。

因为这些是预配置上下文侦听器,所以没有其他可用的 bean,例如DataSource。因此,如果应从数据库读取属性,则必须手动构建数据源和连接(最终是单独的数据源连接查找)。


选项:ApplicationEnvironmentPreparedEvent 的 ApplicationListener

构建一个应用程序监听器的实现ApplicationEnvironmentPreparedEvents and

将其注册在META-INF/spring.factories和钥匙org.springframework.context.ApplicationListener

- or -

use the SpringApplicationBuilder:

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

Example

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 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.

Example

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 http://docs.spring.io/spring-boot/docs/current/reference/html/howto-spring-boot-application.html#howto-customize-the-environment-or-application-context


选项:ApplicationContextInitializer

这是所有“非 Boot”Spring 开发人员的老朋友。然而,SpringApplication首先模拟配置。

构建一个实现ApplicationContextInitializer and

将其注册在META-INF/spring.factories和钥匙org.springframework.context.ApplicationContextInitializer.

- or -

use the SpringApplicationBuilder:

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

Example

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);
  }

}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何构建基于数据库的Spring Boot环境/属性源? 的相关文章

随机推荐