目标是使用包含密钥和环境的环境运行Spring Boot应用程序。由数据库连接(DataSource)加载和生成的值。
或者,更抽象的定义:虽然文件配置应该是首选(更快,更容易,更宽容......),但有时您会发现需要基于非静态文件的配置的用例。
Spring 3.1引入了Environment
,它实际上是属性解析器(扩展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
。任何environment.getProperty()
都不会获利。
这或多或少与选项1相同(更少魔法,更多选项)。
你知道更好的选择吗?理想情况下,解决方案将使用上下文数据源。但是,这在概念上是个问题,因为数据源bean的创建依赖于属性本身......
答案 0 :(得分:3)
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()
以及属性占位符中可用。
因为这些是预配置上下文侦听器,所以没有其他bean可用,例如DataSource
。因此,如果应该从数据库中读取属性,则必须手动构建数据源和连接(最终是分离的数据源连接查找)。
选项:ApplicationEnvironmentPreparedEvent的ApplicationListener
构建使用ApplicationEnvironmentPreparedEvent
和
在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
这是所有人的老朋友&#34; non Boot&#34; 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);
}
}