繁体   English   中英

Micronaut 中的不可变配置和 YAML 中的列表

[英]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 行添加断点并调试测试以查看会发生什么。 当第二个例子的返回类型是Instanceintercept(ctx)方法通过@ConfigurationProperties注解识别出Instance类型是ConfigurationAdvice的成员。 在这种情况下,它使用拦截器从Instance接口调用每个方法 - 它适用于MyConfig接口的相同机制。 当对List getInstances()方法进行相同的检查时,它不会将List类型识别为ConfigurationAdvice的一部分,因此它将其视为常规类型,没有包含在任何拦截器中,而是直接用作任何其他班级。

暂无
暂无

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

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