列出Spring Boot中的分布式属性

问题描述

下面的

代码示例来自一个Spring Boot应用程序,该应用程序从本地application.properties文件获取属性定义。可从远程Consul服务器获得其他或覆盖的属性定义。目的是使应用程序枚举所有属性定义,而不管其来源如何。设置示例:

本地application.properties文件包含此属性定义 spring.cloud.stream.bindings.breakoutOutputChannel.destination = breakout_topic_local

Consul服务器中相同属性的主要定义是: spring.cloud.stream.bindings.breakoutOutputChannel.destination = breakout_topic_production

在同一代码中,我使用两种机制来访问相同的属性值。首先是使用@Value批注,其次是查询从环境获得的属性获取应用程序环境属性方法也如下所示。

使用@Value批注时,将获得正确的覆盖值。但是,当直接访问属性定义时,仅本地文件中的值可用。示例如下。

是否可以获得与@Value注释使用的属性相同的Map,以便可以枚举其内容

示例代码

    @Value("${spring.cloud.stream.bindings.breakoutOutputChannel.destination}")
    String breakout;
    
    @EventListener
    public void onApplicationEvent(ApplicationReadyEvent  event) {
         Properties props =applicationProperties(event.getApplicationContext().getEnvironment());
         log.info("Property spring.cloud.stream.bindings.breakoutOutputChannel.destination");
         log.info( " value from bound var = " +breakout);
         log.info( " value from properties = " +props.getProperty("spring.cloud.stream.bindings.breakoutOutputChannel.destination"));
    }
    
    public Properties applicationProperties(Environment env) {
        final Properties properties = new Properties();
        for(Iterator it = ((AbstractEnvironment) env).getPropertySources().iterator(); it.hasNext(); ) {
            PropertySource propertySource = (PropertySource) it.next();
            if (propertySource instanceof PropertiesPropertySource) {
                log.info("Adding all properties contained in " + propertySource.getName());
                properties.putAll(((MapPropertySource) propertySource).getSource());
            }
            if (propertySource instanceof  CompositePropertySource){
                properties.putAll(getPropertiesInCompositePropertySource((CompositePropertySource) propertySource));
            }
        }
        return properties;
    }

    private Properties getPropertiesInCompositePropertySource(CompositePropertySource compositePropertySource){
        final Properties properties = new Properties();
        compositePropertySource.getPropertySources().forEach(propertySource -> {
            if (propertySource instanceof MapPropertySource) {
                log.info("Adding all properties contained in " + propertySource.getName());
                properties.putAll(((MapPropertySource) propertySource).getSource());
            }
            if (propertySource instanceof CompositePropertySource)
                properties.putAll(getPropertiesInCompositePropertySource((CompositePropertySource) propertySource));
        });
        return properties;
    }

样本输出

Adding all properties contained in applicationConfig: [file:./application.properties]
Adding all properties contained in applicationConfig: [classpath:/application.properties]
Property spring.cloud.stream.bindings.breakoutOutputChannel.destination
 value from bound var = breakout_topic_production
 value from properties = breakout_topic_local

解决方法

在上面的示例中创建的生成的属性对象的来源是应用程序环境,而这些值的确仅是包含在本地属性定义文件中的那些值。

但是,如果使用属性键直接从环境中获取值,则返回的值将包含从Consul分发给应用程序的任何替代。在示例中,返回值来自

event.getApplicationContext().getEnvironment().getProperty("spring.cloud.stream.bindings.breakoutOutputChannel.destination"));

将返回正确的属性值。