说明 我正在尝试将以下配置与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类看起来像这样-
@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是类型安全的,并且已经分别测试了Enums,Map和POJO的绑定。但是同时实现这三个功能是我无法实现的。
答案 0 :(得分:2)
请在您的内部类Resource和APIConfig中添加 static 例如:
public static class Resource {
private String name;
private String apiPath;
private String jsonPath;
}
答案 1 :(得分:1)
您可以这样做:
像这样创建POJO:
@Getter
@Setter
@ConfigurationProperties("platform.service")
public class DogProperties {
private Map<String, APIConfig> config;
}
在DogConfig中,您可以执行此操作以获取属性:
@Configuration
@EnableConfigurationProperties(DogProperties.class)
public class DogConfig {
@Autowire
private DogProperties properties
...
@Bean
@Qualifier("guardConfig")
public APIConfig guardConfig(){
return properties.get("guard");
}
}
如果您查看此示例,则秘密是一个Map属性,您可以使用键盘锁来解析地图,也可以使用APIConfig播放。
答案 2 :(得分:1)
已编辑:
您可以使用ConstructorBinding注释(请参见https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config-constructor-binding)将属性绑定到Constructor,以使用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-Databind ObjectMapper将具有附加地图值类型的有线地图转换为具有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的组合似乎不起作用。您可能必须将两种可能的结构之一转换为所需的结构。
答案 3 :(得分:0)
将用于访问整个结构的主类: 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;
}
ConfigType
public enum ConfigType {
guard, play
}
ServiceConfig
import com.fasterxml.jackson.annotation.JsonProperty; // this is the trick
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ServiceConfig {
@JsonProperty("hostname")
private String hostname;
@JsonProperty("resources")
private List<NamedApiPath> resources;
}
NamedApiPath
import com.fasterxml.jackson.annotation.JsonProperty; // this is the trick
import java.util.Optional;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class NamedApiPath {
@JsonProperty("name")
private String name;
@JsonProperty("api-path")
private String apiPath;
@JsonProperty(value = "json-path", required = false) // bit extra verbose
private Optional<String> jsonPath = Optional.empty();
}