【发布时间】:2014-05-14 09:35:54
【问题描述】:
我的应用遇到配置和属性管理问题。
基本上,我有许多属性文件,每个文件都包含许多属性(键/值)。
另一方面,我的应用使用了许多 Spring 环境配置文件(“dev”、“test”等)。
所有应用配置文件的大部分属性都相同,但有些属性却不同。
我们的想法是在一个地方定义所有属性并让每个配置文件根据配置文件要求覆盖这些属性。这就是我遇到问题的地方......
我尝试将PropertySourcesPlaceholderConfigurer 作为要覆盖的属性的基本源(不绑定到任何特定配置文件),然后将多个PropertyOverrideConfigurer 每个绑定到将覆盖基本源的特定配置文件。
这是我现在的配置:
@Configuration
public class PropertyConfigurerConfiguration {
static class defaultConfiguration {
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() throws IOException {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertySourcesPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(Boolean.TRUE);
propertySourcesPlaceholderConfigurer.setLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:META-INF/props/*.properties"));
return propertySourcesPlaceholderConfigurer;
}
}
@Profile(Profiles.DEV)
static class devConfiguration {
@Bean
public static PropertyOverrideConfigurer propertyOverrideConfigurer() throws IOException {
PropertyOverrideConfigurer propertyOverrideConfigurer = new PropertyOverrideConfigurer();
propertyOverrideConfigurer.setIgnoreResourceNotFound(Boolean.TRUE);
propertyOverrideConfigurer.setLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:META-INF/props/dev/*.properties"));
return propertyOverrideConfigurer;
}
}
@Profile(Profiles.TEST)
static class testConfiguration {
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() throws IOException {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertySourcesPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(Boolean.TRUE);
propertySourcesPlaceholderConfigurer.setLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:META-INF/props/*.properties"));
Properties localProperties = new Properties();
localProperties.setProperty("google_api.key", "TEST-API-KEY");
propertySourcesPlaceholderConfigurer.setProperties(localProperties);
propertySourcesPlaceholderConfigurer.setLocalOverride(Boolean.TRUE);
return propertySourcesPlaceholderConfigurer;
}
@Bean
public static PropertyOverrideConfigurer propertyOverrideConfigurer() throws IOException {
PropertyOverrideConfigurer propertyOverrideConfigurer = new PropertyOverrideConfigurer();
propertyOverrideConfigurer.setIgnoreResourceNotFound(Boolean.TRUE);
propertyOverrideConfigurer.setLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:META-INF/props/test/*.properties"));
return propertyOverrideConfigurer;
}
}
...
请注意,我必须专门为测试配置文件重新定义 PropertySourcesPlaceholderConfigurer 并使用本地覆盖。这只是一个临时的 hack,我想摆脱它......
因此我遇到的问题如下:
我不能在基础
PropertySourcesPlaceholderConfigurer中考虑并覆盖任意键。似乎它必须遵循beanName.property模式,或者我必须使用setIgnoreInvalidKeys,但随后将不会考虑任意键。例如我不能拥有诸如application.url=http://localhost:8080/myApp之类的属性并在@Component 中使用它...此外,类似 jpa 属性映射的问题也可能出现:
查看以下配置:
entityManagerFactoryBean.setJpaPropertyMap(propertiesMap());
private Map<String, String> propertiesMap() {
Map<String, String> propertiesMap = new HashMap<>();
propertiesMap.put("hibernate.dialect", "org.hibernate.dialect.MySQL5InnoDBDialect");
propertiesMap.put("hibernate.hbm2ddl.auto", "update");
propertiesMap.put("hibernate.ejb.naming_strategy", "org.hibernate.cfg.ImprovedNamingStrategy");
propertiesMap.put("hibernate.connection.charSet", "UTF-8");
propertiesMap.put("hibernate.show_sql", ???);
propertiesMap.put("hibernate.format_sql", ???);
propertiesMap.put("hibernate.use_sql_comments", ???);
return propertiesMap;
}
覆盖配置器施加的约束似乎不允许覆盖hibernate.format_sql等属性...
然后我的问题是:我如何使用PropertyOverrideConfigurer 来满足我的应用程序要求(见上文)。还是有另一种解决方案来覆盖PropertySourcesPlaceholderConfigurer?
编辑 1:
我修改了配置,但在上下文启动时遇到了新问题:
Caused by: java.io.FileNotFoundException: Could not open ServletContext resource [/classpath*:META-INF/props/dev/app-config.properties]
at org.springframework.web.context.support.ServletContextResource.getInputStream(ServletContextResource.java:141)
at org.springframework.core.io.support.EncodedResource.getInputStream(EncodedResource.java:143)
at org.springframework.core.io.support.PropertiesLoaderUtils.fillProperties(PropertiesLoaderUtils.java:98)
at org.springframework.core.io.support.PropertiesLoaderUtils.fillProperties(PropertiesLoaderUtils.java:72)
at org.springframework.core.io.support.PropertiesLoaderUtils.loadProperties(PropertiesLoaderUtils.java:58)
at org.springframework.core.io.support.ResourcePropertySource.<init>(ResourcePropertySource.java:64)
at org.springframework.context.annotation.ConfigurationClassParser.processPropertySource(ConfigurationClassParser.java:323)
at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:227)
at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:205)
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:173)
at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:241)
at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:205)
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:182)
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:152)
... 18 more
这是我修改后的课程:
@Configuration
@PropertySource(name = "default-configuration", value = { "classpath*:META-INF/props/app-config.properties", "classpath*:META-INF/props/database.properties",
"classpath*:META-INF/props/email.properties" })
public class PropertyConfigurerConfiguration {
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertySourcesPlaceholderConfigurer.setIgnoreResourceNotFound(Boolean.TRUE);
propertySourcesPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(Boolean.TRUE);
return propertySourcesPlaceholderConfigurer;
}
@Configuration
@Profile(Profiles.DEV)
@PropertySource(name = "dev-configuration", value = { "classpath*:META-INF/props/dev/app-config.properties", "classpath*:META-INF/props/dev/database.properties",
"classpath*:META-INF/props/dev/email.properties" })
public static class DevConfiguration {
}
@Configuration
@Profile(Profiles.TEST)
@PropertySource(name = "test-configuration", value = { "classpath*:META-INF/props/test/app-config.properties", "classpath*:META-INF/props/test/database.properties" })
public static class TestConfiguration {
}
}
编辑 2:我通过将所有 classpath*:xx 更改为 classpath:xx 对编辑 1 中描述的问题进行了排序。但是我注意到 dev 属性源不会覆盖默认值,即使用默认 PropertySource 中的键,而 dev PropertySource 中存在相同的键...
【问题讨论】:
-
在使用@PropertySource 注解时,spring 似乎不允许使用诸如
*.properties或classpath*:之类的通配符。 -
关于 edit2,尝试添加另一个内部
@Configuration类,并将其添加到带有@Profile注释的@Configuration类之后。 (见我修改后的答案)。 -
我尝试了您修改后的答案(甚至将默认配置移到顶部,以便开发人员覆盖默认配置),但它仍然无法正常工作(它只选择默认配置)。这是我如何激活个人资料
spring.profiles.active=dev... -
我会假设要维护顺序,但这可能在 jdk/平台组合上有所不同。我建议使用
ApplicationContextInitializer解决方案,更灵活,更强大,您可以保证文件的顺序。 -
非常感谢马丁。那么我可能不得不使用上下文初始化程序......我很惊讶地看到一个属性源很难用相同的键覆盖另一个。 :-( 基本上这就是我想要实现的目标......