问题记录-枚举类型使用

问题记录-枚举类型使用

缘起

最近在做一个埋点需求,需求对于后端很简单,就是保存数据即可。和以往不同的是在一个同事自研的项目上做开发,框架结构没有老项目完善,很多内容写的比较粗糙,分层也没那么严格。

因此我也开发“自由”一点,在构建前端传入的 reqDto 时,有个“触发事件”字段,一开始我按照习惯定义为字符串。在编写随后对应枚举类的时,想起某次看到将枚举类作为字段类型规范在请求体当中。这样做是不是更规范?我是不是也可以尝试这么做?

问题

于是乎,我将“触发事件”的类型由字符串替换成枚举。好处是直观,再也不用去翻这个触发事件类型的枚举在哪定义。

1
2
3
4
5
6
7
 @NotBlank(message = "触发时间不能为空。")
private String triggerEvent;

// 由上文的字符串替换为下文的枚举类型↓

@NotBlank(message = "触发时间不能为空。")
private TrackedGuideEventEnum triggerEvent;

但问题也随之而来,我像往常一样在业务层去校验字段是否符合枚举类的要求,当我故意发送错误的枚举类型时,发现没有任何报错,校验代码也未生效,请求日志显示“触发事件”字段内容并没有传递到业务层。

这样的结果并不是我想要的,我理想情况是请求非预期事件类型,系统提示对应报错,并停止向下运行代码。

解决

经过一番排查,发现原因是 Spring Boot 在接收到一个 JSON 请求时,Jackson 库会尝试将 JSON 字符串 “triggerEvent
“ : “CLICK_THE_MEMBER” 转换为 TrackedGuideEventEnum 类型的字段,如果找不到,默认行为不是抛出异常,而是将该字段的值设为 null。

AI 提示我可以变更 spring-Jackson 的全局配置:

1
2
3
4
spring:
jackson:
deserialization:
read-unknown-enum-values-as-null: false

这样做影响太大,我决定采取方案二,使用@JsonCreator 自定义反序列化逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
// 在枚举类当中加入如下静态方法

@JsonCreator
public static TrackedGuideEventEnum fromValue(String value) {
if (value == null) {
throw new IllegalArgumentException("触发事件(triggerEvent)不能为空值");
}
return Stream.of(TrackedGuideEventEnum.values())
.filter(p -> p.name().equalsIgnoreCase(value))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("无效的触发事件: '" + value + "'"));
}

上述代码可以在枚举序列化的时候,采取 @JsonCreator 规定的序列化校验,自动去检测并且提示报错,然而当我再次测试时,结果又一次出乎我的意料:

1
"errorMessage": "JSON parse error: create enum error, enumClass com.vicutu.plugins.local.enums.TrackedGuideEventEnum, paramValue VIEW_MEMBER_MANAG111EMENT_PAE, offset 153, character \", line 6, column 10, fastjson-version ......省略其他内容

好消息是确实抛出异常了,坏消息是异常太糟糕了,并没有按照我期望的打印:

“”无效的触发事件: ‘“ + value + “‘“”

这样可读性高的内容。

再次分析原因,发现是异常处理时,会优先处理json框架的异常,其次才是具体的异常内容。而我的项目因为本身没有统一的全局异常管理去拆开异常,导致抛出的异常只会是包裹在json框架异常。

要彻底解决这个问题,最好的办法就是在项目当中加入统一的全局异常管理,自定义异常类型。不过最后考虑到实际开发任务时间以及对项目的熟悉程度,只能暂时先放弃这个想法,老老实实的将数据类型改成字符串,果然一切都顺畅起来。


问题记录-枚举类型使用
https://wuzu15hao.com/post/Problem Record - Enumeration Type Usage/
作者
winter
发布于
2025年8月25日
许可协议