不同配置项列表的 Spring 配置 [英] Spring Configuration for a List of differently configured items
问题描述
我希望使用 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:
记得让所有嵌套的配置类
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);
}
}
- 在配置类中添加缺少的构造函数.如果您希望
ShapeFactory.draw
方法使用单线构建对象,则必须添加专门定制的构造函数来执行此操作.否则,您可以使用默认构造函数构建对象并一一设置属性.例如.前者:
- 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);
}
}
- 编辑主要配置
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屋!