简体   繁体   English

spring 引导配置中列表的环境变量

[英]Environment variables for list in spring boot configuration

For my Spring Boot application, I am trying to use an environment variable that holds the list of properties.topics in application.yml (see configuration below).对于我的 Spring 引导应用程序,我正在尝试使用一个环境变量来保存application.yml中的properties.topics列表(请参阅下面的配置)。

properties:
      topics:
        - topic-01
        - topic-02
        - topic-03

I use the configuration file to populate properties bean (see this spring documentation ), as shown below我使用配置文件来填充属性 bean(请参阅此spring 文档),如下所示

import java.util.ArrayList;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("properties")
public class ApplicationProperties {
  private List<String> topics = new ArrayList<>();
  public void setTopics(List<String> topics) {
     this.topics = topics;
  }
  public List<String> getTopics() {
     return this.topics;
  }
}

With the use of environment variable, I can have the list's content change without changing the application.yml .通过使用环境变量,我可以在不更改application.yml的情况下更改列表的内容。 However, all examples that I could find so far only for cases where an environment variable holding only single value, not a collection of values in my case.但是,到目前为止,我能找到的所有示例仅适用于环境变量仅包含单个值,而不是我的情况下的值集合的情况。

Edit:编辑:

To make it clear after @vancleff's comment, I do not need the values of the environment variable to be saved to application.yml .为了在@vancleff 的评论之后说清楚,我不需要将环境变量的值保存到application.yml

Another edit:另一个编辑:

I think by oversimplifying my question, I shoot myself in the foot.我认为通过过度简化我的问题,我会在自己的脚下开枪。 @LppEdd answer works well with the example given in my question. @LppEdd 答案适用于我的问题中给出的示例。 However, what happens if instead of a collection of simple string topic names, I need a bit more complex structure.但是,如果我需要更复杂的结构而不是简单的字符串主题名称的集合,会发生什么情况。 For example, something like例如,像

properties:
  topics:
    - 
      name: topic-01
      id: id-1
    - 
      name: topic-02
      id: id-2
    - 
      name: topic-03
      id: id-3

a bit late for the show but, I was facing the same problem and this solves it节目有点晚了,但是我遇到了同样的问题,这解决了

https://github.com/spring-projects/spring-boot/wiki/Relaxed-Binding-2.0#lists-1 https://github.com/spring-projects/spring-boot/wiki/Relaxed-Binding-2.0#lists-1

MY_FOO_1_ = my.foo[1]

MY_FOO_1_BAR = my.foo[1].bar

MY_FOO_1_2_ = my.foo[1][2]`

So, for the example in the question:因此,对于问题中的示例:

properties:
  topics:
    - 
      name: topic-01
      id: id-1
    - 
      name: topic-02
      id: id-2
    - 
      name: topic-03
      id: id-3

The environment variables should look like this:环境变量应如下所示:

PROPERTIES_TOPICS_0_NAME=topic-01
PROPERTIES_TOPICS_0_ID=id-01
PROPERTIES_TOPICS_1_NAME=topic-02
PROPERTIES_TOPICS_1_ID=id-02
PROPERTIES_TOPICS_2_NAME=topic-03
PROPERTIES_TOPICS_2_ID=id-03

Suggestion, don't overcomplicate.建议,不要太复杂。

Say you want that list as an Environment variable.假设您希望该列表作为Environment变量。 You'd set it using你可以使用

-Dtopics=topic-01,topic-02,topic-03

You then can recover it using the injected Environment Bean, and create a new List<String> Bean然后您可以使用注入的Environment Bean 恢复它,并创建一个新的List<String> Bean

@Bean
@Qualifier("topics")
List<String> topics(final Environment environment) {
    final var topics = environment.getProperty("topics", "");
    return Arrays.asList(topics.split(","));
}

From now on, that List can be @Autowired .从现在开始,该List可以是@Autowired
You can also consider creating your custom qualifier annotation, maybe @Topics .您还可以考虑创建自定义限定符注释,也许是@Topics

Then然后

@Service
class TopicService {
   @Topics
   @Autowired
   private List<String> topics;

   ...
}

Or even甚至

@Service
class TopicService {
   private final List<String> topics;

   TopicService(@Topics final List<String> topics) {
      this.topics = topics;
   }

   ...
}

What you could do is use an externalized file.您可以做的是使用外部化文件。
Pass to the environment parameters the path to that file.向环境参数传递该文件的路径。

-DtopicsPath=C:/whatever/path/file.json

Than use the Environment Bean to recover that path.而不是使用Environment Bean 来恢复该路径。 Read the file content and ask Jackson to deserialize it读取文件内容并要求Jackson反序列化

You'd also need to create a simple Topic class您还需要创建一个简单的Topic

public class Topic {
    public String name;
    public String id;
}

Which represents an element of this JSON array代表此JSON数组的一个元素

[
    {
        "name": "topic-1",
        "id": "id-1"
    },
    {
        "name": "topic-2",
        "id": "id-2"
    }
]

@Bean
List<Topic> topics(
        final Environment environment,
        final ObjectMapper objectMapper) throws IOException {
    // Get the file path
    final var topicsPath = environment.getProperty("topicsPath");

    if (topicsPath == null) {
        return Collections.emptyList();
    }

    // Read the file content
    final var json = Files.readString(Paths.get(topicsPath));

    // Convert the JSON to Java objects
    final var topics = objectMapper.readValue(json, Topic[].class);
    return Arrays.asList(topics);
}

在此处输入图片说明

Also facing the same issue , fixed with having a array in deployment.yaml from values.yml replacing the default values of application.yml

example as : 

deployment.yml -

----------------

env: 
            - name : SUBSCRIBTION_SITES_0_DATAPROVIDER
              value: {{ (index .Values.subscription.sites 0).dataprovider | quote }}
            - name: SUBSCRIBTION_SITES_0_NAME
              value: {{ (index .Values.subscription.sites 0).name | quote }}

values.yml -

---------------

  subscription:
    sites:
      - dataprovider: abc
        name: static

application.yml -

------------------

  subscription:
    sites:
      - dataprovider: ${SUBSCRIBTION_SITES_0_DATAPROVIDER:abcd}
        name: ${SUBSCRIBTION_SITES_0_NAME:static}
    
Java Code :

@Getter
@Setter
@Configuration
@ConfigurationProperties(prefix = "subscription")
public class DetailsProperties {

    private List<DetailsDto> sites;

    DetailsProperties() {
        this.sites = new ArrayList<>();
    }

}

Pojo mapped :

@Getter
@Setter
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class DetailsDto {
    private String dataprovider;
    private String name;
}

I built a quick little utility to do this.我建立了一个快速的小实用程序来做到这一点。

import java.util.LinkedList;
import java.util.List;

import org.springframework.core.env.Environment;

/**
 * Convenience methods for dealing with properties.
 */
public final class PropertyUtils {

  private PropertyUtils() {
  }

  public static List<String> getPropertyArray(Environment environment, String propertyName) {
    final List<String> arrayPropertyAsList = new LinkedList<>();
    int i = 0;
    String value;
    do {
      value = environment.getProperty(propertyName + "[" + i++ + "]");
      if (value != null) {
        arrayPropertyAsList.add(value);
      }
    } while (value != null);

    return arrayPropertyAsList;
  }

}

You could modify this without too many changes to support multiple fields as well.您可以在不进行太多更改的情况下对其进行修改以支持多个字段。 I've seen similar things done to load an array of database configurations from properties.我见过类似的事情来从属性加载一组数据库配置。

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

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