【问题标题】:Binding ConfigurationProperties to Map of <Enum,Pojo>将 ConfigurationProperties 绑定到 <Enum,Pojo> 的映射
【发布时间】:2019-03-03 14:07:24
【问题描述】:

说明 我正在尝试将以下配置与我的 Component 类绑定-

platform:
  service:
    config:
      guard:
       hostname: fancy-host1.kiki.com
       resources:
         - name: bark
           api-path: dog/alert/bark/{dog-id}
         - name: bite
           api-path: dog/alert/bite/{dog-id}
           json-path: $..kill-mode
      play:
        hostname: fancy-host2.kiki.com
        resources:
         - name: lick
           api-path: dog/chill/lick/{dog-id}
           json-path: $..cute-mode

我的组件类看起来像这样-

@Component
@ConfigurationProperties(prefix = "platform.service")
public class DogConfig
{
    @Getter
    @Setter
    public class Resource
    {
        private String name;
        private String apiPath;
        private String jsonPath;
    }

    @Getter
    @Setter
    public class APIConfig
    {
        private String hostname;
        private List<Resource> resources = new ArrayList<>();
    }

    private Map<ServiceType, APIConfig> config = new LinkedHashMap<>();

    public Map<ServiceType, APIConfig> getConfig()
    {
        return config;
    }

    public void setConfig(Map<ServiceType, APIConfig> config)
    {
        this.config = config;
    }
}

在上面的代码中,ServiceType 是一个具有值 GUARD 和 PLAY 的枚举。

问题 虽然我的 Spring Boot 应用程序在初始化时没有抛出任何错误,但它没有将我的 YAML 绑定到 DogConfig 类。我不确定我到底错过了什么。

到目前为止我的故障排除工作 我依靠this spring doc 来外部化我的配置。我知道@ConfigurationProperties 是类型安全的,并且已经单独测试了枚举、映射和 POJO 的绑定。但同时拥有这三个是我无法实现的。

【问题讨论】:

    标签: java spring-boot


    【解决方案1】:

    请在 Resource 和 APIConfig 的内部类中添加 static 例如:

    public static class Resource {
        private String name;
        private String apiPath;
        private String jsonPath;
    }
    

    【讨论】:

      【解决方案2】:

      你可以这样做:

      1. 像这样创建一个 POJO:

        @Getter
        @Setter
        @ConfigurationProperties("platform.service")
        public class DogProperties {
        
            private Map<String, APIConfig> config;
        
        }
        
      2. 在您的 DogConfig 中,您可以这样做以获取属性:

        @Configuration
        @EnableConfigurationProperties(DogProperties.class)
        public class DogConfig {
        
            @Autowire
            private DogProperties properties
        
            ...
        
            @Bean
            @Qualifier("guardConfig")
            public APIConfig guardConfig(){
               return properties.get("guard");
           }
        
         }
        

      如果你看这个例子,secret 是一个 Map 属性,你可以使用带有 key Guard 的 map 解析或使用 APIConfig。

      【讨论】:

        【解决方案3】:

        已编辑:

        您可以像这样使用 ConstructorBinding 注释(参见 https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config-constructor-binding)将属性绑定到构造函数,以使用 valueOf() 方法将字符串键转换为枚举键:

        @Component
        @ConfigurationProperties(prefix = "platform.service")
        @ConstructorBinding
        public class DogConfig {
        
            DogConfig(Map<String, APIConfig> config) {
               this.config = config.entrySet().stream().collect(
                                Collectors.toMap(
                                   e -> ServiceType.valueOf(e.getKey()), 
                                   Map.Entry::getValue
                                )
                        );
            }
        
        ...
        

        或者这样,通过额外使用Jackson-DatabindObjectMapper 将具有附加映射值类型的有线映射转换为具有pojo值类型的映射:

        @Component
        @ConfigurationProperties(prefix = "platform.service")
        @ConstructorBinding
        public class DogConfig {
        
            DogConfig(Map<ServiceType, Map<String,Object> > config) {
               this.config = config.entrySet().stream().collect(
                                Collectors.toMap(
                                   Map.Entry::getKey,
                                   e -> (new ObjectMapper()).convertValue(e.getValue(),APIConfig.class)
                                )
                        );
            }
        
        ...
        

        就像OP 已经暗示的那样,Enum 和 Pojo 在地图中的组合似乎不起作用。您可能必须将两种可能的结构中的任何一种转换为所需的结构。

        【讨论】:

        • 您能否逐步分享解决方案,另外如何在代码中调用相同的函数以获得更好的想法这将有助于其他人理解其他根据您的解决方案实现相同的功能,并可能有助于更多描述方式。
        • 完成。如果我确实理解你的话,基本的 Java 和 Spring Boot 基础知识的描述仍然缺失,因此交付:)
        【解决方案4】:

        用于访问整个结构的主类: PlatformConfigContainer

        import java.util.Map;
        import lombok.AllArgsConstructor;
        import lombok.Data;
        import org.springframework.boot.context.properties.ConfigurationProperties;
        import org.springframework.stereotype.Component;
        
        @Data
        @AllArgsConstructor
        @ConfigurationProperties(prefix = "platform.service")
        @Component
        public class PlatformConfigContainer {
          private final Map<ConfigType, ServiceConfig> config;
        }
        

        配置类型

        public enum ConfigType {
          guard, play
        }
        

        服务配置

        import java.util.List;
        import lombok.AllArgsConstructor;
        import lombok.Data;
        import lombok.NoArgsConstructor;
        
        @Data
        @NoArgsConstructor
        @AllArgsConstructor
        public class ServiceConfig {
          private String hostname;
          private List<NamedApiPath> resources;
        }
        

        NamedApiPath

        import java.util.Optional;
        import lombok.AllArgsConstructor;
        import lombok.Data;
        import lombok.NoArgsConstructor;
        
        @Data
        @AllArgsConstructor
        @NoArgsConstructor
        public class NamedApiPath {
        
          private String name;
        
          private String apiPath;
        
          private Optional<String> jsonPath = Optional.empty();
        }
        

        使用 Spring Boot 2.3.7.RELEASE 测试

        【讨论】:

          猜你喜欢
          • 2018-06-09
          • 2014-01-20
          • 2018-08-01
          • 2019-09-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多