可以通过Spring配置文件选择@PropertySources吗?

时间:2012-10-02 14:03:45

标签: spring

我有一个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配置中,但在使用EnvironmentgetRequiredProperty()方法时则不行。

<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支持。

6 个答案:

答案 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为我指引正确的方向。