简体   繁体   English

如何将Spring配置为部分和可选地覆盖属性?

[英]How do I configure Spring to partially and optionally override properties?

I would like to have a properties setup which can, on certain environments, override specific properties. 我希望有一个属性设置,在某些环境中,它可以覆盖特定的属性。 For example, our default JDBC properties for dev are: 例如,dev的默认JDBC属性是:

  • db.driverClassName=com.mysql.jdbc.Driver db.driverClassName = com.mysql.jdbc.Driver
  • db.url=jdbc:mysql://localhost:3306/ourdb = db.url配置参数JDBC:MySQL的://本地主机:3306 / ourdb
  • db.username=root db.username =根
  • db.password= db.password =

The problem is that some of our devs would like to have a different username/password on the db, or possibly even a non locally hosted db. 问题是我们的一些开发人员希望在数据库上拥有不同的用户名/密码,甚至可能是非本地托管的数据库。 The same is true for our rabbitMQ configuration, which currently uses a similar localhost, guest/guest setup. 我们的rabbitMQ配置也是如此,它目前使用类似的localhost,guest / guest设置。 Being able to override the properties of certain elements of this configuration on a per-developer basis would allow us to move much of the infrastructure/installation requirements for building the software off the local machine and onto dedicated servers. 能够在每个开发人员的基础上覆盖此配置的某些元素的属性将允许我们移动大部分基础架构/安装要求,以便从本地计算机和专用服务器上构建软件。

I have set-up a simple project to wrap my head around the configuration required to achieve what I want, and this is my first foray into the world of spring property configuration, since up till now, property loading and management is done with some custom code. 我已经设置了一个简单的项目来围绕实现我想要的配置,这是我第一次进入spring属性配置的世界,因为到目前为止,属性加载和管理是通过一些自定义完成的码。 Here is my setup: 这是我的设置:

class Main_PropertyTest {
    public static void main(String[] args) {
        String environment = System.getenv("APPLICATION_ENVIRONMENT"); // Environment, for example: "dev"
        String subEnvironment = System.getenv("APPLICATION_SUB_ENVIRONMENT"); // Developer name, for example: "joe.bloggs"
        System.setProperty("spring.profiles.active", environment);
        System.setProperty("spring.profiles.sub", subEnvironment);

        try(AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(PropertyTestConfiguration.class)) {
            Main_PropertyTest main = context.getBean(Main_PropertyTest.class);
            main.printProperty();
        }
    }

    private final String property;

    public Main_PropertyTest(String property) {
        this.property = property;
    }

    public void printProperty() {
        System.out.println("And the property is: '" + property + "'.");
    }
}

And my configuration: 我的配置:

@Configuration
public class PropertyTestConfiguration {
    @Bean
    public static PropertySourcesPlaceholderConfigurer primaryPlaceholderConfigurer() {
        PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
        propertySourcesPlaceholderConfigurer.setLocation(new ClassPathResource(System.getProperty("spring.profiles.active") + ".main.properties"));
        return propertySourcesPlaceholderConfigurer;
    }

    @Bean
    public static PropertySourcesPlaceholderConfigurer secondaryPlaceholderConfigurer() {
        PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
        propertySourcesPlaceholderConfigurer.setLocation(new ClassPathResource(System.getProperty("spring.profiles.sub") + ".main.properties"));
        propertySourcesPlaceholderConfigurer.setIgnoreResourceNotFound(true);
        propertySourcesPlaceholderConfigurer.setIgnoreResourceNotFound(true);
        propertySourcesPlaceholderConfigurer.setOrder(-1);
        return propertySourcesPlaceholderConfigurer;
    }

    @Bean
    public Main_PropertyTest main_PropertyTest(@Value("${main.property}") String property) {
        Main_PropertyTest main_PropertyTest = new Main_PropertyTest(property);
        return main_PropertyTest;
    }
}

And for completeness, my dev.main.properties and test.main.properties: 为了完整起见,我的dev.main.properties和test.main.properties:

main.property=dev

main.property=test

The main problem is that I get an illegal argument exception. 主要问题是我得到了一个非法的争论异常。 As far as I can tell, what I have written should be the javaconfig equivalent of this method: http://taidevcouk.wordpress.com/2013/07/04/overriding-a-packaged-spring-application-properties-file-via-an-external-file/ Unfortunately I get the following error: java.lang.IllegalArgumentException: Could not resolve placeholder 'main.property' in string value "${main.property}". 据我所知,我写的应该是javaconfig相当于这种方法: http ://taidevcouk.wordpress.com/2013/07/04/overriding-a-packaged-spring-application-properties-file- via-an-external-file /不幸的是我收到以下错误:java.lang.IllegalArgumentException:无法解析字符串值“$ {main.property}”中的占位符'main.property'。 Note that I also need to take care of the case where there is no sub-environment, and this is the case I have started with (although I get the same error even if both files exist). 请注意,我还需要处理没有子环境的情况,这是我已经开始的情况(尽管即使两个文件都存在,我也会得到相同的错误)。 If I remove the bean which sets up the second propertysourcesplaceholderconfigurer, then it all works fine (by which I mean dev.main.properties is loaded and "And the property is: 'dev'." is printed out). 如果我删除了设置第二个propertysourcesplaceholderconfigurer的bean,那么一切正常(我的意思是加载dev.main.properties并且“并且属性为:'dev'。”打印出来)。

A secondary problem is that the code doesn't look great, and each layer of the system will need two PSPC's set-up so that they can access these properties. 第二个问题是代码看起来不太好,系统的每一层都需要两个PSPC的设置,以便他们可以访问这些属性。 Furthermore, it requires a lot of manual calls to System.getProperty(), since I couldn't pass ${spring.profiles.active} to PSPC.setLocation(); 此外,它需要大量手动调用System.getProperty(),因为我无法将$ {spring.profiles.active}传递给PSPC.setLocation();

Note: I have tried @PropertySources({primaryproperties, secondaryProperties}), but this fails because secondaryProperties does not exist. 注意:我尝试过@PropertySources({primaryproperties,secondaryProperties}),但由于secondaryProperties不存在而失败。 I have also tried @Autowired Environment environment; 我也试过@Autowired Environment环境; and getting the properties from that, but the secondary PSPC causes the environment to not be autowired... 并从中获取属性,但辅助PSPC导致环境无法自动装配...

So following this lengthy explanation, my questions are: 所以经过这个冗长的解释,我的问题是:

  • Is this the right way of solving this problem? 这是解决这个问题的正确方法吗?
  • If so, what is wrong with my configuration? 如果是这样,我的配置有什么问题?
  • How can I simplify the configuration (if at all)? 如何简化配置(如果有的话)?
  • Is there an alternative mechanism available which would solve my problem? 是否有可用的替代机制可以解决我的问题?

Thank you for your time! 感谢您的时间! :) :)

Your configuration is flawed when configuring BeanFactoryPostProcessor with java config the methods should be static. 使用java config配置BeanFactoryPostProcessor时,您的配置存在缺陷,方法应该是静态的。 However it can be even easier, instead of registering your own PropertySourcesPlaceholderConfigurer utilize the default @PropertySource support. 但是,它可以更容易,而不是注册您自己的PropertySourcesPlaceholderConfigurer利用默认的@PropertySource支持。

Rewerite your jav config to the following 将您的jav配置转换为以下内容

@Configuration
@PropertySource(name="main", value= "${spring.profiles.active}.main.properties")
public class PropertyTestConfiguration {

    @Autowired
    private Environment env;

    @PostConstruct
    public void initialize() {
        String resource = env.getProperty("spring.profiles.sub") +".main.properties";
        Resource props = new ClassPathResource(resource);
        if (env instanceof ConfigurableEnvironment && props.exists()) {
            MutablePropertySources sources = ((ConfigurableEnvironment) env).getPropertySources();
            sources.addBefore("main", new ResourcePropertySource(props)); 
        }
    }

    @Bean
    public Main_PropertyTest main_PropertyTest(@Value("${main.property}") String property) {
        Main_PropertyTest main_PropertyTest = new Main_PropertyTest(property);
        return main_PropertyTest;
    }
}

This should first load the dev.main.properties and additionally the test.main.properties which will override the earlier loaded properties (when filled ofcourse). 这应首先加载dev.main.properties以及test.main.properties ,它将覆盖先前加载的属性(当填充时)。

I had a similar issue with overwriting already existing properties in integration tests 我在覆盖集成测试中已经存在的属性时遇到了类似的问题

I came up with this solution: 我想出了这个解决方案:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {
    SomeProdConfig.class,
    MyWebTest.TestConfig.class
})
@WebIntegrationTest
public class MyWebTest {


    @Configuration
    public static class TestConfig {

        @Inject
        private Environment env;


        @PostConstruct
        public void overwriteProperties() throws Exception {
            final Map<String,Object> systemProperties = ((ConfigurableEnvironment) env)
                .getSystemProperties();
            systemProperties.put("some.prop", "test.value");
        }
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM