我在使用我的应用时遇到了配置和属性管理问题。
基本上我有很多属性文件,每个文件都包含许多属性(键/值)。
另一方面,我的应用使用了许多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
并使用本地覆盖。这只是一个暂时的黑客,我想摆脱......
因此我遇到的问题如下:
我不能将任意密钥考虑在内并覆盖基础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中存在相同的键...
答案 0 :(得分:1)
出于某种原因,它看起来很复杂。为什么不简单地为每个配置文件添加@PropertySource
注释?包括默认值。
@Configuration
public class PropertyConfigurerConfiguration {
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
@Configuration
@Profile(Profiles.DEV)
@PropertySource(name="dev-configuration", value="classpath*:META-INF/props/dev/*.properties")
public static class DevConfiguration{}
@Configuration
@Profile(Profiles.TEST)
@PropertySource(name="test-configuration", value="classpath*:META-INF/props/test/*.properties")
public static class DevConfiguration{}
@Configuration
@PropertySource(name="default-configuration", value="classpath*:META-INF/props/*.properties")
public static class DefaultConfiguration() {}
}
这将始终加载默认值并根据配置文件添加其他配置文件,这应该覆盖默认配置中的属性。
另一种解决方案是创建ApplicationContextInitializer
,在其PropertySource
方法中添加initialize
。
public class MyInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private static String DEFAULT_CONFIG_LOCATION= "classpath*:META-INF/props/*.properties";
public void initialize(ConfigurableApplicationContext applicationContext) {
ConfigurableEnvironment env = applicationContext.getEnvironment();
MutablePropertySources mps = env.getPropertySources();
String[] profiles = env.getActiveProfiles();
for (String profile : profiles) {
String location = "classpath*:META-INF/props/"+profile+"/*.properties";
Resource[] resources = applicationContext.getResources(location);
if (resources != null) {
for (Resource res: resources) {
mps.addLast(new ResourcePropertySource(res));
}
}
}
// Load defaults as last
Resource[] resources = applicationContext.getResources(DEFAULT_CONFIG_LOCATION);
if (resources != null) {
for (Resource res: resources) {
mps.addLast(new ResourcePropertySource(res));
}
}
}
}
然后,您可以在web.xml中将其注册为globalInitializerClasses
,并将其应用于此应用程序中创建的所有ApplicationContext
。这也意味着您可以删除@PropertySource
注释和专用配置。这样做的好处是,如果您创建新的配置文件,则无需添加另一个@Configuration
类来加载资源。
如果要替换hibernate属性,则必须首先使用Environment
添加属性。
@Autowired
private Environment env;
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", env.getProperty("hibernate.show_sql", Boolean.class, false));
propertiesMap.put("hibernate.format_sql", env.getProperty("hibernate.format_sql", Boolean.class, false));
propertiesMap.put("hibernate.use_sql_comments", env.getProperty("hibernate.use_sql_comments", Boolean.class, false));
return propertiesMap;
}
这将从Environment
获取属性,从而从已配置的PropertySource
获取属性,如果未找到,则在这种情况下将使用默认false
。