我有一个Spring 3.1 @Configuration
需要一个属性foo
来构建一个bean。该属性在defaults.properties
中定义,但如果应用程序具有活动的overrides.properties
Spring配置文件,则可能会被override
中的属性覆盖。
没有覆盖,代码看起来像这样,并且工作......
@Configuration
@PropertySource("classpath:defaults.properties")
public class MyConfiguration {
@Autowired
private Environment environment;
@Bean
public Bean bean() {
...
// this.environment.getRequiredProperty("foo");
...
}
}
我希望@PropertySource
classpath:overrides.properties
取决于@Profile("overrides")
。有没有人对如何实现这一点有任何想法?我考虑过的一些选项是重复@Configuration
,但这会违反DRY或ConfigurableEnvironment
的程序化操作,但我不确定environment.getPropertySources.addFirst()
调用的去向。< / p>
如果我直接使用@Value
注入属性,则将以下内容置于XML配置中,但在使用Environment
和getRequiredProperty()
方法时则不行。
<context:property-placeholder ignore-unresolvable="true" location="classpath:defaults.properties"/>
<beans profile="overrides">
<context:property-placeholder ignore-unresolvable="true" order="0"
location="classpath:overrides.properties"/>
</beans>
更新
如果您现在尝试这样做,请查看Spring Boot的YAML support,特别是'使用YAML代替属性'部分。那里的配置文件支持会使这个问题没有实际意义,但还没有@PropertySource
支持。
答案 0 :(得分:45)
在静态内部类中添加重写@PropertySource
。不幸的是,您必须一起指定所有属性源,这意味着创建“默认”配置文件作为“覆盖”的替代。
@Configuration
public class MyConfiguration
{
@Configuration
@Profile("default")
@PropertySource("classpath:defaults.properties")
static class Defaults
{ }
@Configuration
@Profile("override")
@PropertySource({"classpath:defaults.properties", "classpath:overrides.properties"})
static class Overrides
{
// nothing needed here if you are only overriding property values
}
@Autowired
private Environment environment;
@Bean
public Bean bean() {
...
// this.environment.getRequiredProperty("foo");
...
}
}
答案 1 :(得分:7)
你可以这样做:
<context:property-placeholder location="classpath:${spring.profiles.active}.properties" />
编辑:如果您需要更高级的东西,可以在应用程序启动时注册PropertySource。
的web.xml
<context-param>
<param-name>contextInitializerClasses</param-name>
<param-value>com.xxx.core.spring.properties.PropertySourcesApplicationContextInitializer</param-value>
</context-param>
你创建的文件:
public class PropertySourcesApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private static final Logger LOGGER = LoggerFactory.getLogger(PropertySourcesApplicationContextInitializer.class);
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
LOGGER.info("Adding some additional property sources");
String[] profiles = applicationContext.getEnvironment().getActiveProfiles()
// ... Add property sources according to selected spring profile
// (note there already are some property sources registered, system properties etc)
applicationContext.getEnvironment().getPropertySources().addLast(myPropertySource);
}
}
完成后,您只需添加上下文:
<context:property-placeholder/>
我无法回答您关于多个配置文件的问题,但我想您在这样的初始化程序上激活它们,并且您可以在配置文件激活期间注册相应的PropertySource项目。
答案 2 :(得分:7)
我建议定义两个文件,其中第二个是可选文件,配置文件为后缀:
@Configuration
@PropertySources({
@PropertySource("classpath:/myconfig.properties"),
@PropertySource(value = "classpath:/myconfig-${spring.profiles.active}.properties", ignoreResourceNotFound = true)
})
public class MyConfigurationFile {
@Value("${my.prop1}")
private String prop1;
@Value("${my.prop2}")
private String prop2;
}
答案 3 :(得分:3)
我想不出除了你建议的艾默生之外的任何其他方式,即在具有@Configuration
注释的单独@Profile
文件中定义此bean:
@Configuration
@Profile("override")
@PropertySource("classpath:override.properties")
public class OverriddenConfig {
@Autowired
private Environment environment;
@Bean
public Bean bean() {
//if..
}
}
答案 4 :(得分:2)
注意:此答案提供了使用@PropertySource
属性文件的备用解决方案。我走这条路是因为尝试使用多个属性文件太麻烦了,这些属性文件可能都有覆盖,同时避免重复代码。
为每组相关属性创建一个POJO接口,以定义其名称和类型。
public interface DataSourceProperties
{
String driverClassName();
String url();
String user();
String password();
}
实现以返回默认值。
public class DefaultDataSourceProperties implements DataSourceProperties
{
public String driverClassName() { return "com.mysql.jdbc.Driver"; }
...
}
每个配置文件的子类(例如开发,生产)并覆盖与默认值不同的任何值。这需要一组互斥的配置文件,但您可以轻松添加“默认”作为“覆盖”的替代。
@Profile("production")
@Configuration
public class ProductionDataSourceProperties extends DefaultDataSourceProperties
{
// nothing to override as defaults are for production
}
@Profile("development")
@Configuration
public class DevelopmentDataSourceProperties extends DefaultDataSourceProperties
{
public String user() { return "dev"; }
public String password() { return "dev"; }
}
最后,将属性配置自动装配到需要它们的其他配置中。这里的优点是您不会重复任何@Bean
创建代码。
@Configuration
public class DataSourceConfig
{
@Autowired
private DataSourceProperties properties;
@Bean
public DataSource dataSource() {
BoneCPDataSource source = new BoneCPDataSource();
source.setJdbcUrl(properties.url());
...
return source;
}
}
我仍然不相信我会坚持使用基于servlet上下文初始化程序中的活动配置文件手动配置属性文件。我的想法是,手动配置不适合单元测试,但我现在不太确定。我更喜欢将属性文件读取到属性访问器列表。
答案 5 :(得分:1)
如果需要支持多个配置文件,可以执行以下操作:
@Configuration
public class Config {
@Configuration
@Profile("default")
@PropertySource("classpath:application.properties")
static class DefaultProperties {
}
@Configuration
@Profile("!default")
@PropertySource({"classpath:application.properties", "classpath:application-${spring.profiles.active}.properties"})
static class NonDefaultProperties {
}
}
这样,您无需为每个配置文件定义静态配置类。 感谢David Harkness为我指引正确的方向。