spring配置用于不同配置项的列表

h5qlskok  于 2021-07-23  发布在  Java
关注(0)|答案(1)|浏览(286)

我希望使用spring配置来动态配置不同对象(形状)的列表。应用程序的每个部署都会使列表发生很大变化,有时总共有一个形状,有时在其他形状中有许多相同形状的变体。
预期配置的一个示例如下所示:

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

到目前为止,我的代码是这样的:

@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;
    }
}

每个形状都可以有自己的结构,但是对它来说最有意义。
使用我现在的代码,当我尝试运行它时,会出现以下错误:


***************************

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属性转换文档,但它看起来像是转换单个属性的指南。我想我正在研究某种转换,以获得一个配置块(每个形状),然后根据类型创建正确的对象。
我熟悉 @JsonTypeInfo 以及 @JsonSubTypes 在使用jackson和json负载时,可能会有一些动态结构化的json,但是我似乎不知道如何为spring配置做类似的事情。
如果有帮助的话,我已经创建了一个github项目,它可以被拉下来轻松地运行这个mre。

wvt8vs2t

wvt8vs2t1#

spring无法知道要示例化哪个特定类,它只会假设您需要一个示例 Shape .
你得到的错误仅仅是由于你忘记声明 Shape 分类为 static .
如果你真的想保留配置的多态性,你必须这样做:
记住创建所有嵌套的配置类 static 添加 ShapeFactory ,能够根据类型确定应示例化哪种形状,例如:

@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);
        }
    }

在配置类中添加缺少的构造函数。如果你想要 ShapeFactory.draw 方法来构建带有一行程序的对象,您必须添加专门定制的构造函数。否则,可以使用默认构造函数构建对象并逐个设置属性。关于前者:

@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);
        }
    }

编辑主配置 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() .

相关问题