52

我有一个 Spring 3.1 @Configuration,它需要一个属性foo来构建一个 bean。如果应用程序具有活动的Spring 配置文件,则该属性在中定义,defaults.properties但可能被该属性覆盖。overrides.propertiesoverride

如果没有覆盖,代码将如下所示,并且可以工作......

@Configuration
@PropertySource("classpath:defaults.properties")
public class MyConfiguration {

    @Autowired
    private Environment environment;

    @Bean
    public Bean bean() {
        ...
        // this.environment.getRequiredProperty("foo");
        ...
    }
}

我想要一个@PropertySourceforclasspath:overrides.properties条件@Profile("overrides")。有没有人对如何实现这一点有任何想法?我考虑过的一些选项是重复的@Configuration,但这会违反 DRY 或对 的编程操作ConfigurableEnvironment,但我不确定environment.getPropertySources.addFirst()调用会去哪里。

如果我直接用 注入属性,则将以下内容放在 XML 配置中有效@Value,但在使用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 支持,特别是“使用 YAML 代替属性”部分。那里的配置文件支持会使这个问题变得毫无意义,但还没有@PropertySource支持。

4

7 回答 7

53

@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");
        ...
    }
}
于 2013-01-05T00:55:20.013 回答
14

我建议,定义两个文件,其中第二个是可选的,配置文件作为后缀:

@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;

}
于 2020-01-07T17:54:09.993 回答
10

你可以做:

  <context:property-placeholder location="classpath:${spring.profiles.active}.properties" />

编辑:如果您需要更高级的东西,您可以在应用程序启动时注册您的 PropertySources。

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 项目。

于 2012-11-09T14:31:55.187 回答
4

如果您需要支持多个配置文件,您可以执行以下操作:

@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让我朝着正确的方向前进。

于 2019-09-02T12:21:03.717 回答
3

除了您建议的 Emerson 之外,我想不出任何其他方法,即在@Configuration带有@Profile注释的单独文件中定义此 bean:

@Configuration
@Profile("override")
@PropertySource("classpath:override.properties")
public class OverriddenConfig {

    @Autowired
    private Environment environment;

    @Bean
    public Bean bean() {
        //if..
    }
}
于 2012-10-02T14:39:06.963 回答
3

注意:此答案提供了将属性文件与@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 上下文初始化程序中的活动配置文件手动配置属性文件。我的想法是进行手动配置不适合单元测试,但我现在不太确定。我真的更喜欢阅读属性文件而不是属性访问器列表。

于 2013-01-05T00:13:45.643 回答
0

这里提到的所有解决方案都有些尴尬,只能与一个配置文件预设一起使用,并且它们不会与更多/其他配置文件一起使用。目前一个 Spring 团队拒绝引入这个特性。但这是我发现的解决方法:

package com.example;

public class MyPropertySourceFactory implements PropertySourceFactory, SpringApplicationRunListener {

    public static final Logger logger = LoggerFactory.getLogger(MyPropertySourceFactory.class);

    @NonNull private static String[] activeProfiles = new String[0];

    // this constructor is used for PropertySourceFactory
    public MyPropertySourceFactory() {
    }

    // this constructor is used for SpringApplicationRunListener
    public MyPropertySourceFactory(SpringApplication app, String[] params) {
    }

    @Override
    public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
        activeProfiles = environment.getActiveProfiles();
    }

    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource encodedResource) throws IOException {
        logger.info("Loading: {} with profiles: {}", encodedResource.toString(), activeProfiles);
        // here you know all profiles and have the source Resource with main
        // properties, just try to load other resoures in the same path with different 
        // profile names and return them as a CompositePropertySource
    }
}

要使其正常工作,您必须具有src/main/resources/META-INF/spring.factories以下内容:

org.springframework.boot.SpringApplicationRunListener=com.example.MyPropertySourceFactory

现在您可以将自定义属性文件放在某处并使用以下命令加载它@PropertySources

@Configuration
@PropertySource(value = "classpath:lib.yml", factory = MyPropertySourceFactory.class)
public class PropertyLoader {
}
于 2021-09-22T10:11:27.680 回答