You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

升级Spring Boot 3.1.1后所有API调用出现TimeZoneNames资源缺失异常

Spring Boot 3.1.1 + Zing Base Java 17 升级后响应追加错误的排查与解决

问题背景

将Spring Boot从2.x版本升级至3.1.1,同时将Java从8升级为Zing Base Java 17后,部分节点上所有API调用出现异常:API本身可正常执行,但实际响应后会追加额外的错误响应,导致客户端报错。该问题甚至在actuator/health接口中也会出现。

响应示例

{"status":"UP"}{"errorCode":"MYAPP-1000","errorMessage":"Can't find bundle for base name sun.util.resources.cldr.TimeZoneNames, locale en_US"}

自定义异常处理器代码

@ExceptionHandler(value = Exception.class )
public ResponseEntity handleGenericException(Exception exception) {
    return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
          .body(cacheRefreshService.buildErrorResponse("MYAPP-1000", exception.getMessage()));
}

核心异常信息

  • 根异常:java.util.MissingResourceException(提示找不到sun.util.resources.cldr.TimeZoneNames资源包)
  • 伴随异常:org.springframework.http.converter.HttpMessageNotWritableException

问题原因

  1. Zing JDK资源包差异:Zing Base Java 17与标准OpenJDK的资源包结构存在区别,sun.util.resources.cldr.TimeZoneNames是OpenJDK中CLDR时区名称资源的路径,但Zing可能未包含该资源包,或资源包的命名/存储路径不同。
  2. 响应流二次写入:API正常执行并写入响应后,Spring在请求收尾阶段(如上下文清理、时区相关处理)触发了MissingResourceException,全局异常处理器捕获该异常后,向已写入内容的响应流追加了错误JSON,导致响应内容拼接混乱。
  3. 全局异常处理器范围过广:@ExceptionHandler(value = Exception.class)捕获了所有异常,包括Spring内部在请求处理后期抛出的底层资源异常,这类异常本不应被暴露给客户端。

解决方法

1. 缩小全局异常处理器的捕获范围

避免捕获所有Exception,仅处理业务相关的自定义异常或预期异常,排除Spring内部框架级异常:

// 仅捕获自定义业务异常
@ExceptionHandler(value = BusinessException.class )
public ResponseEntity handleBusinessException(BusinessException exception) {
    return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
          .body(cacheRefreshService.buildErrorResponse("MYAPP-1000", exception.getMessage()));
}

// 针对性处理常见运行时异常
@ExceptionHandler(value = {IllegalArgumentException.class, IllegalStateException.class})
public ResponseEntity handleCommonExceptions(RuntimeException exception) {
    return ResponseEntity.status(HttpStatus.BAD_REQUEST)
          .body(cacheRefreshService.buildErrorResponse("MYAPP-2000", exception.getMessage()));
}

2. 补充Zing JDK缺失的时区资源

若必须依赖sun.util.resources.cldr.TimeZoneNames相关功能,可手动补充资源:

  • 从OpenJDK 17发行包中提取sun.util.resources.cldr相关的资源文件(.properties或.class)
  • 将这些资源打包成JAR,放入应用类路径
  • 检查Zing JDK配置,确认是否有启用CLDR资源的开关,开启对应配置

3. 替换Sun内部API依赖

检查应用中是否直接/间接依赖sun.util.resources.cldr.TimeZoneNames的代码,替换为标准JDK API,避免依赖非公开的Sun内部类:

// 使用标准API获取时区名称
ZoneId zoneId = ZoneId.of("America/New_York");
String displayName = zoneId.getDisplayName(TextStyle.FULL, Locale.US);

4. 避免向已提交的响应流写入内容

在全局异常处理器中判断响应是否已提交,若已提交则不再写入错误内容:

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(value = Exception.class )
    public ResponseEntity handleGenericException(Exception exception) {
        ServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        // 响应已提交时,直接抛出异常由容器处理,不追加内容
        if (response != null && response.isCommitted()) {
            throw new RuntimeException(exception);
        }
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
              .body(cacheRefreshService.buildErrorResponse("MYAPP-1000", exception.getMessage()));
    }
}

内容的提问来源于stack exchange,提问作者Nishant Modi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 21:35:58