springboot加载本地application.yml和加载Consul中的application.yml配置反序列化LocalDate类型差异

遇到的问题以及解决方式

配置如下:

yml 复制代码
test:
  test-time: 2025-12-31

实体如下:

java 复制代码
@Data
public class Test{
	private LocalDate testTime;
}

springBoot读取本地配置文件后,可以正常解析为LocalDate类型,而读取Consul中的配置后会报错如下:

log 复制代码
Failed to bind properties under 'project.check-in-multiple-coin.test-time' to java.time.LocalDate:

    Property: project.test.test-time
    Value: "Wed Dec 31 08:00:00 CST 2025"
    Origin: "project.test.test-time" from property source "config/demo/test/"
    Reason: org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.util.Date] to type [java.time.LocalDate]

解决方法很简单,将配置改为如下:

yml 复制代码
test:
  test-time: '2025-12-31'

探究两者差异的原因

二者都是通过YamlProcessor这个类来创建Ymal对象的。差异主要是创建Ymal的方式:

consul是直接调用YamlProcessor的createYaml方法,将日期标签转换成了Date,而springboot是通过OriginTrackedYamlLoader类重写了createYaml方法:

OriginTrackedYamlLoader类中的createYaml针对日期类标签做了特殊处理,不使用snakeyaml处理,而是将日期类的标签统一返回为字符串,然后在后续的操作中根据实体的属性类型进行了转换。

Consul处理流程如下:

Constructor继承自SafeConstructor,SafeConstructor对以下标签规定了处理方式:

ConstructYamlTimestamp 类一顿操作之后返回了一个Date类型。

java 复制代码
public static class ConstructYamlTimestamp extends AbstractConstruct {

    private Calendar calendar;

    public Calendar getCalendar() {
      return calendar;
    }

    @Override
    public Object construct(Node node) {
      ScalarNode scalar = (ScalarNode) node;
      String nodeValue = scalar.getValue();
      Matcher match = YMD_REGEXP.matcher(nodeValue);
      if (match.matches()) {
        String year_s = match.group(1);
        String month_s = match.group(2);
        String day_s = match.group(3);
        calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
        calendar.clear();
        calendar.set(Calendar.YEAR, Integer.parseInt(year_s));
        // Java's months are zero-based...
        calendar.set(Calendar.MONTH, Integer.parseInt(month_s) - 1); // x
        calendar.set(Calendar.DAY_OF_MONTH, Integer.parseInt(day_s));
        return calendar.getTime();
      } else {
        match = TIMESTAMP_REGEXP.matcher(nodeValue);
        if (!match.matches()) {
          throw new YAMLException("Unexpected timestamp: " + nodeValue);
        }
        String year_s = match.group(1);
        String month_s = match.group(2);
        String day_s = match.group(3);
        String hour_s = match.group(4);
        String min_s = match.group(5);
        // seconds and milliseconds
        String seconds = match.group(6);
        String millis = match.group(7);
        if (millis != null) {
          seconds = seconds + "." + millis;
        }
        double fractions = Double.parseDouble(seconds);
        int sec_s = (int) Math.round(Math.floor(fractions));
        int usec = (int) Math.round((fractions - sec_s) * 1000);
        // timezone
        String timezoneh_s = match.group(8);
        String timezonem_s = match.group(9);
        TimeZone timeZone;
        if (timezoneh_s != null) {
          String time = timezonem_s != null ? ":" + timezonem_s : "00";
          timeZone = TimeZone.getTimeZone("GMT" + timezoneh_s + time);
        } else {
          // no time zone provided
          timeZone = TimeZone.getTimeZone("UTC");
        }
        calendar = Calendar.getInstance(timeZone);
        calendar.set(Calendar.YEAR, Integer.parseInt(year_s));
        // Java's months are zero-based...
        calendar.set(Calendar.MONTH, Integer.parseInt(month_s) - 1);
        calendar.set(Calendar.DAY_OF_MONTH, Integer.parseInt(day_s));
        calendar.set(Calendar.HOUR_OF_DAY, Integer.parseInt(hour_s));
        calendar.set(Calendar.MINUTE, Integer.parseInt(min_s));
        calendar.set(Calendar.SECOND, sec_s);
        calendar.set(Calendar.MILLISECOND, usec);
        return calendar.getTime();
      }
    }
  }

总结

分析以上差异点并不是为了证明谁对谁错,而是要即知其然也要知其所以然

相关推荐
齐 飞5 分钟前
Spring Data JPA快速入门
spring boot
计算机学姐7 分钟前
基于SpringBoot的高校体育场馆预约系统【个性化推荐算法+数据可视化统计】
java·vue.js·spring boot·后端·mysql·信息可视化·推荐算法
Coder_Boy_10 分钟前
基于SpringAI的在线考试系统设计-用户管理模块设计
java·大数据·人工智能·spring boot·spring cloud
奋进的芋圆22 分钟前
SerialCommManager 详解:从嵌入式通信管理器到 Spring Boot 后端服务
java·spring boot·接口隔离原则
奋进的芋圆22 分钟前
Spring Boot + RAG 项目中集成 MCP 接口技术文档
java·spring boot·ai
苏小瀚36 分钟前
[JavaEE] SpringBoot 配置文件
数据库·spring boot·java-ee
无心水43 分钟前
【分布式利器:腾讯TSF】2、腾讯微服务框架TSF实战指南:Spring Boot零侵入接入与容器化部署全流程
java·spring boot·分布式·微服务·springcloud·分布式利器·腾讯tsf
苏小瀚1 小时前
[JavaEE] Spring Boot 日志
java·spring boot·后端
木子江L1 小时前
SpringBoot集成RabbitMQ消息中间件
java·spring boot·rabbitmq·java-rabbitmq
Gao_xu_sheng1 小时前
IDEA恢复删除模块pom文件
java·spring boot·idea