Spring Boot中含final字段且无默认构造函数的Bean初始化方案
解决Spring Boot中初始化含final字段且无默认构造函数类的ConfigurationProperties Map问题
这个问题我之前帮不少开发者解决过,刚好是Spring Boot处理不可变配置类的典型场景——既要保持SomeClass的不可变性(全final字段),又要让@ConfigurationProperties能正确初始化它的Map实例,给你两个可行的方案,优先推荐第一种,简单又符合最佳实践:
方案一:使用@ConstructorBinding(Spring Boot 2.2+ 推荐)
Spring Boot 2.2之后专门引入了@ConstructorBinding注解来支持不可变配置类,完美适配你的场景,不需要默认构造函数,直接通过带参构造完成字段初始化。
步骤1:修改SomeClass添加@ConstructorBinding
import org.springframework.boot.context.properties.ConstructorBinding; @ConstructorBinding public class SomeClass { private final int field1; private final String field2; // 仅保留带参构造,参数名要和配置文件中的key对应 public SomeClass(int field1, String field2) { this.field1 = field1; this.field2 = field2; } // 只需要getter方法,不需要setter(final字段无法修改) public int getField1() { return field1; } public String getField2() { return field2; } }
步骤2:配置SomeConfigurationProperties
确保开启配置属性支持,两种方式任选其一:
- 方式A:在配置类上添加
@EnableConfigurationProperties
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration; import java.util.HashMap; import java.util.Map; @Configuration @EnableConfigurationProperties @ConfigurationProperties(prefix = "some") public class SomeConfigurationProperties { private Map<String, SomeClass> map = new HashMap<>(); // 仅需要getter方法,Spring会自动绑定Map内容 public Map<String, SomeClass> getMap() { return map; } }
- 方式B:直接给
SomeConfigurationProperties添加@Component注解(适合自动扫描的场景)
步骤3:配置文件示例(application.yml)
按照层级结构编写配置,Spring会自动通过构造函数映射字段:
some: map: first-entry: field1: 123 field2: "first-value" second-entry: field1: 456 field2: "second-value"
注意:如果你的项目编译时没有保留参数名(比如没用Java 8+的
-parameters编译参数),可以给构造函数参数添加@JsonProperty("field1")来明确对应配置key,避免绑定失败。
方案二:自定义Converter(适配Spring Boot 2.2以下版本)
如果你的Spring Boot版本低于2.2,无法使用@ConstructorBinding,可以通过自定义类型转换器来实现Map到SomeClass的转换。
实现自定义转换器
import org.springframework.core.convert.converter.Converter; import org.springframework.stereotype.Component; import java.util.Map; @Component public class SomeClassConverter implements Converter<Map<String, Object>, SomeClass> { @Override public SomeClass convert(Map<String, Object> source) { // 手动处理配置中的字段映射,注意类型转换 int field1 = ((Number) source.get("field1")).intValue(); String field2 = (String) source.get("field2"); return new SomeClass(field1, field2); } }
注册这个转换器后,Spring在处理@ConfigurationProperties中的Map时,会自动调用它把配置中的键值对转换成SomeClass实例。
关键说明
- 方案一的
@ConstructorBinding是Spring官方推荐的不可变配置类解决方案,代码更简洁,也符合不可变对象的设计原则。 - 两种方案都不需要XML配置,完全适配Spring Boot的注解驱动开发模式。
内容的提问来源于stack exchange,提问作者milosdju
相关产品推荐
相关产品推荐

