[英]Mapped the Yaml configuration to Micronaut swagger open API security configuration
[英]Immutable configuration in Micronaut with list in YAML
给出然后遵循 YAML 配置
a:
b:
instances:
- name: T1
endpoint: "http://t1"
version: "1.5.3.24305,2021-08-09 18:01"
read-timeout: 20
- name: T2
endpoint: "http://t1"
version: "2.0.0.16555,2022-01-03 16:48"
read-timeout: 20
我想使用 Micronaut 2.5.13 将它绑定到我的不可变配置界面。
@ConfigurationProperties("a.b")
public interface MyConfig {
List<Instance> getInstances();
interface Instance {
String getName();
URL getEndpoint();
String getVersion();
int getReadTimeout();
}
}
当我然后运行我的 Spock 集成测试时
@MicronautTest
class MyConfigSpec extends Specification {
@Inject
MyConfig config
void "Make sure the config has instances"() {
expect:
config.instances.isEmpty() == false
}
}
由于列表为空,因此断言失败。 如何告诉 Micronaut 将instances
列表映射到我的不可变Instance
对象中?
该问题是由缺少TypeConverter<S,T>
引起的TypeConverter<S,T>
它知道如何从另一种类型创建MyConfig.Instance
的实例。 属性源解析器将abinstances
配置视为List<Map>
,因此对于每个条目,它都会尝试找到满足Map
-> MyConfig.Instance
的转换器。 因为没有人明确满足这种转换,所以回退到Map
-> Object
类型转换器。 这个转换器使用了 Jackson 的序列化和反序列化,它也不起作用,因为 Jackson 不知道如何反序列化这样的 JSON:
{
"name": "T1",
"endpoint": "http://t1",
"version": "1.5.3.24305,2021-08-09 18:01",
"read-timeout": 20
}
到MyConfig.Instance
的实例,因为没有可用于 Jackson 反序列化器的接口的实现。
实现TypeConverter<Map, MyConfig.Instance>
bean 可以解决这个问题。 它可以返回一个匿名类,该类实现了从输入映射返回所有值的接口。
import io.micronaut.context.annotation.ConfigurationProperties;
import io.micronaut.core.convert.ConversionContext;
import io.micronaut.core.convert.TypeConverter;
import javax.inject.Singleton;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@ConfigurationProperties("a.b")
public interface MyConfig {
List<Instance> getInstances();
interface Instance {
String getName();
URL getEndpoint();
String getVersion();
int getReadTimeout();
}
@Singleton
class MapToInstanceConverter implements TypeConverter<Map, Instance> {
@Override
public Optional<Instance> convert(Map object, Class<Instance> targetType, ConversionContext context) {
return Optional.of(new Instance() {
@Override
public String getName() {
return object.getOrDefault("name", "").toString();
}
@Override
public URL getEndpoint() {
try {
return new URI(object.getOrDefault("endpoint", "").toString()).toURL();
} catch (MalformedURLException | URISyntaxException e) {
throw new RuntimeException(e);
}
}
@Override
public String getVersion() {
return object.getOrDefault("version", "").toString();
}
@Override
public int getReadTimeout() {
return Integer.parseInt(object.getOrDefault("read-timout", 0).toString());
}
});
}
}
}
现在运行测试时,您将看到MyConfig.getInstances()
方法返回的两个条目。
“等等” ,您可能会问, “为什么当我定义Instance getInstance()
方法而不需要指定任何类型转换器时它会起作用?” . 这是正确的问题。 您可能已经尝试过,以下示例运行良好,无需提供任何额外的类型转换器:
@ConfigurationProperties("a.b")
public interface MyConfig {
Instance getInstance();
@ConfigurationProperties("instance")
interface Instance {
String getName();
URL getEndpoint();
String getVersion();
int getReadTimeout();
}
}
这两个示例之间的主要区别在于,前一个示例使用具有泛型类型List<Instance>
。 不可变配置机制使用ConfigurationIntroductionAdvice.intercept(ctx)
方法来调用您在接口中定义的每个 getter 方法。 这里的问题是泛型类型在运行时被删除,因此getInstances()
方法的返回类型被识别为List
,而不是List<Instance>
。 您可以在ConfigurationIntroductionAdvice
类的第 66 行添加断点并调试测试以查看会发生什么。 当第二个例子的返回类型是Instance
, intercept(ctx)
方法通过@ConfigurationProperties
注解识别出Instance
类型是ConfigurationAdvice
的成员。 在这种情况下,它使用拦截器从Instance
接口调用每个方法 - 它适用于MyConfig
接口的相同机制。 当对List getInstances()
方法进行相同的检查时,它不会将List
类型识别为ConfigurationAdvice
的一部分,因此它将其视为常规类型,没有包含在任何拦截器中,而是直接用作任何其他班级。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.