java - 如何配置 Spring 来部分或选择性地覆盖属性?

标签 java spring properties spring-java-config

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

  • db.driverClassName=com.mysql.jdbc.Driver
  • db.url=jdbc:mysql://localhost:3306/ourdb
  • db.username=root
  • db.password=

问题是我们的一些开发人员希望在数据库上使用不同的用户名/密码,甚至可能是非本地托管的数据库。我们的rabbitMQ配置也是如此,它当前使用类似的本地主机、 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

主要问题是我遇到了非法参数异常。据我所知,我写的应该是这个方法的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 并打印出“And the property is: 'dev'.”)。

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

注意:我尝试过@PropertySources({primaryproperties, secondaryProperties}),但是失败,因为 secondaryProperties 不存在。我也尝试过@AutowiredEnvironment环境;并从中获取属性,但辅助 PSPC 导致环境无法自动连接...

在这个冗长的解释之后,我的问题是:

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

感谢您的宝贵时间! :)

最佳答案

使用 java 配置配置 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,它将覆盖之前加载的属性(当然是在填充时)。

关于java - 如何配置 Spring 来部分或选择性地覆盖属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20212558/

相关文章:

ios - @property copy 与 readonly 结合使用是否有意义?

java - 在java中无法将数组作为参数传递

java - Hibernate 是更新而不是插入,为什么?

java - 如何使用缩略图调整图像大小时将黑色背景替换为白色?

spring - Maven多模块项目中的集成测试

c# - 带有序列化的伪只读属性

Python @property 设计

java - 在 Java EE 项目中,删除我的类后,我无法取回它们?

java - Spring无效属性异常

java - @ControllerAdvice 类中的信息传递到哪里