Spring Boot项目中application.yml自定义类List无法读取的问题求助
问题描述
我在Spring Boot 2.3.4版本的项目里读取application.yml配置时碰到了个棘手的问题:一开始所有配置都读成null,清理重建项目后其他配置都正常了,但自定义类的List<LogComponent>始终还是null。试了各种办法都没解决,麻烦帮忙看看代码里哪里出问题了?
相关代码
配置类 TestAPIConfiguration
@Configuration @EnableConfigurationProperties @ConfigurationProperties public class TestAPIConfiguration { private String eventCache; private String diskBasedCache; private List<String> sendAllSMSto; private List<String> sendAllEmailsto; // 这个属性明明yml里有值,但始终读成null private List<LogComponent> logComponents; @NotNull private String selfURIPrefix; @NotNull private String investURIPrefix; @NotNull private String ifaURIPrefix; private String apiEnv; private final Joiner joiner = Joiner.on( "," ).skipNulls(); private static final Logger LOGGER = LoggerFactory.getLogger(TestAPIConfiguration.class); @PostConstruct public void setSystemProperties() { try { System.setProperty(SystemConstants.EVENT_CACHE_PATH, eventCache); System.setProperty(SystemConstants.DISK_BASED_CACHE_PATH, diskBasedCache); System.setProperty(SystemConstants.REQUEST_LOGGING_FIELDS, JSONUtils.getObjectMapper().writeValueAsString(logComponents)); System.setProperty(SystemConstants.ENVIRONMENT_IDENTIFIER, apiEnv); System.setProperty(INVEST_URI_PREFIX, investURIPrefix); System.setProperty(IFA_URI_PREFIX, ifaURIPrefix); if(sendAllSMSto != null) System.setProperty(SEND_ALL_SMS_TO, joiner.join(sendAllSMSto)); if(sendAllEmailsto != null) System.setProperty(SystemConstants.SEND_ALL_EMAILS_TO, joiner.join(sendAllEmailsto)); } catch(Exception se) { LOGGER.error("Error in Configuration Setup: {}", se.getLocalizedMessage()); } } public String getEventCache() { return eventCache; } public void setEventCache(String eventCache) { this.eventCache = eventCache; } public String getDiskBasedCache() { return diskBasedCache; } public void setDiskBasedCache(String diskBasedCache) { this.diskBasedCache = diskBasedCache; } public List getSendAllSMSto() { return sendAllSMSto; } public void setSendAllSMSto(List<String> sendAllSMSto) { this.sendAllSMSto = sendAllSMSto; } public List getSendAllEmailsto() { return sendAllEmailsto; } public void setSendAllEmailsto(List<String> sendAllEmailsto) { this.sendAllEmailsto = sendAllEmailsto; } public List getRequestLoggingFields() { return logComponents; } public void setRequestLoggingFields(List<LogComponent> requestLoggingFields) { this.logComponents = requestLoggingFields; } public String getSelfURIPrefix() { return selfURIPrefix; } public void setSelfURIPrefix(String selfURIPrefix) { this.selfURIPrefix = selfURIPrefix; } public String getInvestURIPrefix() { return investURIPrefix; } public void setInvestURIPrefix(String investURIPrefix) { this.investURIPrefix = investURIPrefix; } public String getIfaURIPrefix() { return ifaURIPrefix; } public void setIfaURIPrefix(String ifaURIPrefix) { this.ifaURIPrefix = ifaURIPrefix; } public String getApiEnv() { return apiEnv; } public void setApiEnv(String apiEnv) { this.apiEnv = apiEnv; } }
自定义类 LogComponent
@Component public class LogComponent { @NotNull private String headerName; @NotNull private String sessionKey; @NotNull private String logPrintKey; public String getHeaderName() { return headerName; } public String getSessionKey() { return sessionKey; } public String getLogPrintKey() { return logPrintKey; } }
application.yml 配置
debug: true server: port: 8080 apiEnv: UAT selfURIPrefix: "https://testurl.localhost.net" investURIPrefix: "https://testurl.mediaserver.net" ifaURIPrefix: "https://testurl.secondaryserver.net" sendAllSMSto: - "0000000000" sendAllEmailsto: - "abc@testmail.com" eventCache: "C:\\Users\\username\\project\\devnull\\eventcachepurchase.mdb" diskBasedCache: "C:\\Users\\username\\project\\devnull\\cache.mdb" logComponents: - headerName: X-RT-REQUEST-TRACKER sessionKey: NOT AVAILABLE logPrintKey: REQUEST-TRACKER - headerName: X-RT-INX-DWD sessionKey: IFX-PDR logPrintKey: PDR_NO - headerName: X-RT-IFA-ARN sessionKey: IRX-AXRN logPrintKey: AXR-CDODEEE
问题排查与原因分析
我帮你梳理了下代码,发现两个核心问题:
- 属性映射不匹配:你在
TestAPIConfiguration里定义的属性是logComponents,但对应的getter是getRequestLoggingFields()、setter是setRequestLoggingFields()。Spring Boot的配置绑定是通过getter/setter的名称来匹配yml中的键的,这俩名字对不上,Spring根本不知道要把yml里的logComponents绑定到这个属性上,自然就返回null了。 - LogComponent缺少赋值必要条件:这个类加了
@Component注解,但作为配置属性的承载类,Spring需要通过setter方法给它的属性赋值,可你现在只有getter没有setter;另外,Spring实例化配置类对象时需要无参构造器,你也没添加,这就导致即使前面的映射对了,Spring也没法实例化并填充LogComponent对象。
解决方案
按照下面的步骤修改代码就能解决问题:
- 修正TestAPIConfiguration的getter/setter命名
把getRequestLoggingFields()和setRequestLoggingFields()改成和属性名一致的getLogComponents()和setLogComponents(),这样Spring才能正确匹配yml里的logComponents键。修改后的代码片段:
public List<LogComponent> getLogComponents() { return logComponents; } public void setLogComponents(List<LogComponent> logComponents) { this.logComponents = logComponents; }
- 修正LogComponent类
- 去掉
@Component注解:这个类是用来承载配置的,不需要被Spring作为Bean管理(除非你有其他特殊用途); - 添加无参构造器:Spring实例化配置类对象时需要;
- 给每个属性添加setter方法:Spring需要通过这些方法给属性赋值。
修改后的LogComponent代码:
public class LogComponent { @NotNull private String headerName; @NotNull private String sessionKey; @NotNull private String logPrintKey; // 添加无参构造器 public LogComponent() {} public String getHeaderName() { return headerName; } public void setHeaderName(String headerName) { this.headerName = headerName; } public String getSessionKey() { return sessionKey; } public void setSessionKey(String sessionKey) { this.sessionKey = sessionKey; } public String getLogPrintKey() { return logPrintKey; } public void setLogPrintKey(String logPrintKey) { this.logPrintKey = logPrintKey; } }
- 可选优化(非必须)
你可以给TestAPIConfiguration的@ConfigurationProperties注解加上prefix,比如@ConfigurationProperties(prefix = "api"),然后把yml里的配置项都加上api.前缀,这样配置结构会更清晰,不过这一步不影响当前问题的解决,看你自己的需求来加就行。
内容的提问来源于stack exchange,提问作者Hitesh Kirtane
相关产品推荐
相关产品推荐

