不同配置项列表的 Spring 配置 [英] Spring Configuration for a List of differently configured items

查看:43
本文介绍了不同配置项列表的 Spring 配置的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望使用 Spring 配置来动态配置不同对象(形状)的列表.该列表可能因应用的每次部署而异,有时总共有 1 个形状,有时相同形状的许多变体以及其他形状.

I'm looking to use Spring configuration to dynamically configure a list of different objects (shapes). The list can vary greatly by each deployment of the app, sometimes having 1 shape in total, sometimes many variations of the same shape among other shapes.

预期配置的一个示例如下所示:

One example of the intended configuration would look something like this:

app.shapes:
  - type: Circle
    radius: 3
    colors:
      - fill: red
        border: green
  - type: Rectangle
    length: 4
    width: 2
  - type: Circle
    radius: 5
    colors:
     - fill: blue
       border: black
  - type: Triangle
    base: 5
    height: 2

我迄今为止的代码如下所示:

and the code I have to date looks like this:

@Slf4j
@SpringBootApplication
public class ConfigListShapesApplication {

    @Autowired
    private ShapesConfig shapesConfig;

    public static void main(String[] args) {
        SpringApplication.run(ConfigListShapesApplication.class, args);
    }

    @PostConstruct
    public void logResults() {
        log.debug("We have {} shapes configured", shapesConfig.getShapes().size());
        for(Shape shape : shapesConfig.getShapes()) {
            log.debug("Shape is of type {} and class is instance of {}", 
                    shape.getType(), shape.getClass().getSimpleName());
        }
    }

    @Data
    @Component
    @ConfigurationProperties(prefix="app")
    public class ShapesConfig {
        private List<Shape> shapes;
    }

    @Data
    public class Shape {
        private String type;
    }

    @Data
    public class Circle extends Shape {
        private Integer radius;
        private Colors colors;
    }

    @Data
    public class Rectangle extends Shape {
        private Integer length;
        private Integer width;
        private Colors colors;
    }

    @Data
    public class Triangle extends Shape {
        private Integer base;
        private Integer height;
    }

    @Data
    public class Colors {
        private String fill;
        private String border;
    }
}

每个形状都可以有自己的配置结构,但最适合它.

Each shape can have its own configuration structured however makes the most sense for it.

使用我现在拥有的代码,当我尝试运行它时,出现以下错误:

With the code I have now, when I try to run it I get the following error:

***************************
APPLICATION FAILED TO START
***************************

Description:

Binding to target [Bindable@3b152928 type = java.util.List<com.example.configlistshapes.ConfigListShapesApplication$Shape>, value = 'provided', annotations = array<Annotation>[[empty]]] failed:

    Property: app.shapes[0].colors[0].border
    Value: green
    Origin: class path resource [application.yml] - 6:17
    Reason: The elements [app.shapes[0].colors[0].border, ....repeat lots...] were left unbound.
    Property: app.shapes[0].colors[0].fill
    Value: red
    Origin: class path resource [application.yml] - 5:15
    Reason: The elements [app.shapes[0].colors[0].border ....repeat lots...] were left unbound.
    Property: app.shapes[0].radius
    Value: 3
.... many more

我看过Spring 属性转换 文档,但它看起来像是转换单个属性的指南.我想我正在考虑进行某种转换以获取配置块(每个形状),然后根据类型创建正确的对象.

I have looked at the Spring Properties Conversion docs, but it looks like guidance for converting a single property. I think I'm looking at some sort of conversion to take a block of configuration (each Shape), then creating the correct object based on the type.

我在与 Jackson 合作时熟悉 @JsonTypeInfo@JsonSubTypes 并且有一个 JSON 有效负载,可能有一些动态结构的 JSON,但我不能似乎想出了如何为 Spring Configuration 做类似的事情.

I am familiar with @JsonTypeInfo and @JsonSubTypes when working with Jackson and having a JSON payload that might have a bit of dynamically structured JSON, though I can't seem to figure out how to do something similar for Spring Configuration.

如果有帮助,我已经创建了一个 GitHub 项目,它可以下拉以轻松运行此MRE.

If it helps, I've created a GitHub project that can be pulled down to easily run this MRE.

推荐答案

Spring 无法知道要实例化哪个特定的类,它只会假设您想要一个 Shape 的实例.

Spring has no way to know which specific class to instantiate, it will just assume you want an instance of Shape.

您得到的错误仅仅是因为您忘记将 Shape 类声明为 static.

The error you got is simply due to the fact that you forgot to declare the Shape class as static.

如果您真的想保留配置的多态性,则必须执行以下操作:

If you really mean to retain the polymorphic nature of your configuration, you'd have to do something like this:

  1. 记得让所有嵌套的配置类static

添加一个ShapeFactory,能够根据类型决定应该实例化哪种形状,例如:

Add a ShapeFactory, able to determine which kind of shape should be instantiated depending on the type, eg.:

    @Data
    public static class ShapeFactory {
        private String type;
        private Integer radius;
        private Colors colors;
        private Integer length;
        private Integer width;
        private Integer base;
        private Integer height;

        public Shape draw() {
            if (type.equals("Circle")) {
                return new Circle(radius, colors, type);
            } else if(type.equals("Rectangle") {
                ...
            } ...
            
            throw new IllegalArgumentException("Invalid Shape type " + type);
        }
    }

  1. 在配置类中添加缺少的构造函数.如果您希望 ShapeFactory.draw 方法使用单线构建对象,则必须添加专门定制的构造函数来执行此操作.否则,您可以使用默认构造函数构建对象并一一设置属性.例如.前者:
  1. Add the missing constructors in the configuration classes. If you want the ShapeFactory.draw method to build objects with one-liners, you will have to add specifically tailored constructors to do so. Otherwise, you can build the object with the default constructor and set the properties one by one. Eg. of the former:

    @Data
    @AllArgsConstructor
    public static class Shape {
        private String type;
    }

    @Data
    public static class Circle extends Shape {
        private Integer radius;
        private Colors colors;

        public Circle(Integer radius, Colors colors, String type) {
            super(type);
            setRadius(radius);
            setColors(colors);
        }
    }

  1. 编辑主要配置ShapesConfig类如下:

    @Data
    @Component
    @ConfigurationProperties(prefix = "app")
    public class ShapesConfig {
        private List<ShapeFactory> shapes;
        private List<Shape> actualShapes;

        @PostConstruct
        public void setUp() {
            setActualShapes(shapes.stream().map(ShapeFactory::draw).collect(Collectors.toList()));
        }
    }

现在可以通过 shapesConfig.getActualShapes() 检索所有形状.

All the shapes can now be retrieved via shapesConfig.getActualShapes().

这篇关于不同配置项列表的 Spring 配置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆