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

我想要一个属性设置,在某些环境中,它可以覆盖特定的属性。 例如,dev的默认JDBC属性是:

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

问题是我们的一些开发人员希望在数据库上拥有不同的用户名/密码,甚至可能是非本地托管的数据库。 我们的rabbitMQ配置也是如此,它目前使用类似的localhost,guest / guest设置。 能够在每个开发人员的基础上覆盖此配置的某些元素的属性将允许我们移动大部分基础架构/安装要求,以便从本地计算机和专用服务器构建软件。

我已经设置了一个简单的项目来围绕实现我想要的配置,这是我第一次进入spring属性配置的世界,因为到目前为止,属性加载和管理是通过一些自定义完成的码。 这是我的设置:

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 + "'."); } } 

我的配置:

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

为了完整起见,我的dev.main.properties和test.main.properties:

 main.property=dev main.property=test 

主要问题是我得到了非法的争论exception。 据我所知,我所写的应该是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’。 请注意,我还需要处理没有子环境的情况,这是我已经开始的情况(尽管即使两个文件都存在,我也会得到相同的错误)。 如果我删除设置第二个propertysourcesplaceholderconfigurer的bean,那么一切正常(我的意思是加载dev.main.properties并且“并且属性为:’dev’。”打印出来)。

第二个问题是代码看起来不太好,系统的每一层都需要两个PSPC的设置,以便他们可以访问这些属性。 此外,它需要大量手动调用System.getProperty(),因为我无法将$ {spring.profiles.active}传递给PSPC.setLocation();

注意:我尝试过@PropertySources({primaryproperties,secondaryProperties}),但由于secondaryProperties不存在而失败。 我也试过@Autowired Environment环境; 并从中获取属性,但辅助PSPC导致环境无法自动assembly…

因此,经过这个冗长的解释,我的问题是:

  • 这是解决这个问题的正确方法吗?
  • 如果是这样,我的配置有什么问题?
  • 如何简化配置(如果有的话)?
  • 是否有可用的替代机制可以解决我的问题?

感谢您的时间! 🙂

使用java config配置BeanFactoryPostProcessor时,您的配置存在缺陷,方法应该是静态的。 但是,它可以更容易,而不是注册您自己的PropertySourcesPlaceholderConfigurer利用默认的@PropertySource支持。

将您的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; } } 

这应首先加载dev.main.properties以及test.main.properties ,它将覆盖先前加载的属性(当填充时)。

我在覆盖集成测试中已经存在的属性时遇到了类似的问题

我提出了这个解决方案:

 @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 systemProperties = ((ConfigurableEnvironment) env) .getSystemProperties(); systemProperties.put("some.prop", "test.value"); } }