如何基于配置属性值注入指定Test子类(SingleTest/ParallelTest)
问题描述
现有如下application.yml配置:
tests: happy-path: - type: single other-property: "other-value" - type: parallel other-property: "another-value"
以及如下TestRunner类(包含Test类型的List):
@ConfigurationProperties(prefix = "test") @EnableConfigurationProperties @ConstructorBinding public class TestRunner { private List<Test> happyPath; public TestRunner(List<Test> tests) { this.tests = tests; } }
Test类有两个子类实现:SingleTest和ParallelTest,均继承自Test。需要配置Spring,使得当tests.happy-path[*].type==single时注入SingleTest实例,tests.happy-path[*].type==parallel时注入ParallelTest实例,最终得到包含这两种子类实例的List<Test>。尝试过Converter但未成功。
解决方案
Spring Boot配置属性绑定支持多态实例化,下面提供两种可行的实现方案:
方案一:利用Jackson多态注解(推荐)
Spring Boot默认用Jackson处理配置属性绑定,直接在父类Test上添加Jackson多态注解,让框架根据type字段自动匹配子类:
- 给
Test类添加多态注解:
import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type" // 对应yml中的type字段 ) @JsonSubTypes({ @JsonSubTypes.Type(value = SingleTest.class, name = "single"), @JsonSubTypes.Type(value = ParallelTest.class, name = "parallel") }) public abstract class Test { private String type; private String otherProperty; // 生成构造器、getter/setter方法 }
- 修正
TestRunner的配置前缀和构造器参数:
@ConfigurationProperties(prefix = "tests") // 注意前缀要和yml根节点"tests"匹配 @EnableConfigurationProperties @ConstructorBinding public class TestRunner { private final List<Test> happyPath; // 构造器参数名要对应yml的"happy-path"(自动转驼峰) public TestRunner(List<Test> happyPath) { this.happyPath = happyPath; } public List<Test> getHappyPath() { return happyPath; } }
- 子类直接继承
Test即可:
public class SingleTest extends Test {} public class ParallelTest extends Test {}
配置完成后,Spring Boot会通过Jackson的多态规则,自动根据type字段实例化对应子类并注入到集合中。
方案二:自定义Converter绑定
如果不想依赖Jackson注解,可以自定义Converter实现类型转换:
- 实现
Converter并添加@ConfigurationPropertiesBinding注解:
import org.springframework.core.convert.converter.Converter; import org.springframework.boot.context.properties.ConfigurationPropertiesBinding; import org.springframework.stereotype.Component; @ConfigurationPropertiesBinding @Component public class TestConverter implements Converter<Map<String, Object>, Test> { @Override public Test convert(Map<String, Object> source) { String type = (String) source.get("type"); Test test; if ("single".equals(type)) { test = new SingleTest(); } else if ("parallel".equals(type)) { test = new ParallelTest(); } else { throw new IllegalArgumentException("未知测试类型: " + type); } // 手动绑定属性 test.setType(type); test.setOtherProperty((String) source.get("other-property")); return test; } }
同样修正
TestRunner的前缀和构造器参数(和方案一的第二步一致)。确保
Test及其子类有对应的setter方法,方便转换器赋值。
关键注意点
- 配置前缀
prefix必须和yml根节点完全匹配(之前的test要改成tests); - 构造器参数名要和yml中的key对应(yml的
happy-path对应Java的驼峰命名happyPath),否则ConstructorBinding无法正确注入; - 方案一需要确保项目引入Jackson依赖(Spring Boot Starter Web默认包含,纯配置项目可手动引入
com.fasterxml.jackson.core:jackson-databind)。
内容的提问来源于stack exchange,提问作者Ramon Rius
相关产品推荐
相关产品推荐

