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();
      }
    }
  }

总结

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

相关推荐
Java_AI工程师8 小时前
Spring4 / SpringBoot1.x 老项目想接入 AI?不用升级框架!3 套无痛落地方案
java·人工智能·spring boot
VimorphTech8 小时前
Java 文件缓存实战:如何避免读到未下载完成的视频?
java·spring boot
十年Java程序媛9 小时前
JVM 内存区域与对象内存布局|new 对象底层流程,对象头 MarkWord 详解
java·jvm·spring boot
linyb极客之路10 小时前
Spring Boot 2升3太痛苦?用OpenRewrite批量搞定,少走90%弯路!
spring boot
171320330计算机毕设编程10 小时前
计算机毕业设计 基于springboot+vue的在线观鸟视频播放网站 LW PPT
java·大数据·vue.js·spring boot·算法·课程设计
星辰云-15 小时前
# 基于Spring Boot的德州特产扒鸡商城系统(毕设|源码|论文|演示视频)
spring boot·后端·课程设计
星辰云-15 小时前
基于Spring Boot的旅游景点票务系统的设计与实现(毕设项目,附完整源码)
spring boot·后端·课程设计
十年Java程序媛16 小时前
JVM 基础与 GC 垃圾回收详解|可达性分析、分代模型、垃圾收集器、STW
java·开发语言·jvm·spring boot·后端·面试
她的男孩17 小时前
JAR 全对仍 checksum mismatch:Forge Admin 把 Flyway 从 filesystem 改成 classpath
java·数据库·spring boot
小蒜学长17 小时前
基于数据挖掘的京东商城商品推荐系统设计与实现(代码+数据库+LW)
java·数据库·spring boot·后端·推荐系统·个性化推荐