Spring Boot Sping Boot 配置中列表的环境变量

wn9m85ua  于 2023-06-22  发布在  Spring
关注(0)|答案(5)|浏览(163)

对于我的Spring Boot应用程序,我尝试使用一个环境变量,该变量在application.yml中保存properties.topics的列表(请参阅下面的配置)。

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

我使用配置文件来填充properties 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;
  }
}

通过使用环境变量,我可以在不更改application.yml的情况下更改列表的内容。然而,到目前为止,我能找到的所有例子都只适用于环境变量只保存单个值的情况,而不是我的情况下的值集合。

    • 编辑:**

在@vancleff的评论之后,为了说明这一点,我不需要将环境变量的值保存到application.yml

    • 另一编辑:**

我想我的问题过于简单化了,我搬起石头砸了自己的脚。@LppEdd的答案与我问题中给出的例子很好地结合在一起。但是,如果我需要的不是简单的字符串主题名称的集合,而是更复杂的结构,会发生什么情况呢?例如,类似于

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

slmsl1lt1#

有点晚了,但我也面临着同样的问题,这解决了它
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]`

所以,对于问题中的例子:

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

环境变量应该如下所示:

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
wnrlj8wa

wnrlj8wa2#

建议,不要太复杂。
假设您希望将该列表作为Environment变量。你会用

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

然后可以使用注入的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(","));
}

从现在开始,List可以是@Autowired
您还可以考虑创建自定义限定符注解,比如@Topics
然后,

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

   ...
}

甚至

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

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

   ...
}

您可以使用外部化的文件。
将该文件的路径传递给环境参数。

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

而不是使用EnvironmentBean来恢复该路径。读取文件内容并要求Jackson将其反序列化
您还需要创建一个简单的Topic

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

,它表示此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);
}

djmepvbi

djmepvbi3#

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

vuv7lop34#

还有一种解决这个问题的方法,它在解析中更丑陋,但非常容易并且有效。替换:

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

有:

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

然后这样读:

private List<CombinedTopic> topicsCombined = new ArrayList<>();
public CombinedTopic(@Value("${properties.topics}") List<String> topics,
                     @Value("${properties.id}") List<String> ids) {
    List<CombinedTopic> collect = IntStream.range(0, ids.size())
        .mapToObj(i -> new CombinedTopic(ids.get(i), topics.get(i)))
            .toList();
}

请注意,我假设ID和Topic必须一起使用,并且顺序相同。

vbopmzt1

vbopmzt15#

我建立了一个快速的小工具来做这件事。

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

}

您可以修改它,而不需要太多更改,以支持多个字段。我见过从属性加载数据库配置数组时所做的类似事情。

相关问题