引言 🌟
在日常开发中,我们会返回数据给前端,但数据格式往往不符合前端的预期。这时我们可以使用
JsonSerialize
对返回的数据进行定制化处理 ------比如将时间戳转换为日期格式、将
BigDecimal
转换为字符串格式等,这些都是比较常见的需求。
然而,在实际开发中,我们存入数据库的数据往往都是与枚举值相对应的编码 (如 "aliyun"
、"dh3t"
),而前端展示时却需要对应的中文名称(如**"阿里云"
** )。如果每次都在 Service 层手动 set 一个**desc
**字段,不仅繁琐,还容易遗漏。
✅ 更优雅的做法是: 利用 Jackson 的
JsonSerializer
,在序列化阶段自动为枚举字段注入对应的描述信息 ,实现类似provider: "aliyun"
+providerDesc: "阿里云"
的自动输出。
-
🎯 本文将带你实现:
-
枚举值自动映射数据库 ✅(借助 MyBatis-Plus)
-
接口返回时自动附加中文描述 ✅(通过自定义序列化器)
-
前后端彻底解耦,新增枚举无需改前端代码 ✅
需要实现的效果如下图:

本文将带你一步步实现这一优雅方案!
🧩 1. 场景还原:我们面临的问题
假设我们有一个短信服务商枚举:
java
public enum SmsProvider {
aliyun("aliyun", "阿里云"),
dh3t("dh3t", "大汉三通");
private String value;
private String desc;
SmsProvider(String value, String desc) {
this.value = value;
this.desc = desc;
}
// getter 省略...
}
数据库中存储的是 value
字段(如 "aliyun"
),但我们希望接口返回时,除了原始字段外,还能自动携带一个 xxxDesc
字段用于展示,比如:
java
{
"provider": "aliyun",
"providerDesc": "阿里云"
}
🎯 目标达成:前端直接使用 providerDesc
展示,无需任何判断逻辑!
⚙️ 2. 核心技术栈准备:MyBatis-Plus 枚举支持
2.1 什么是 IEnum<T>
?
📌 IEnum<T>
是 MyBatis-Plus 提供的枚举接口,用于实现数据库字段与 Java 枚举之间的自动映射。
com.baomidou.mybatisplus.core.enums.IEnum
当你使用 IEnum
时:
- 查询数据库 → 自动映射为对应枚举对象
- 插入/更新 → 自动提取
.getValue()
写入数据库
💡 它让"数据库值 ↔ 枚举对象"转换变得透明无感!
2.2 自定义通用枚举接口:IOxEnum
为了统一管理带描述的枚举类型,我们创建一个扩展接口:
java
public interface IOxEnum extends IEnum<String> {
/**
* 获取枚举的中文描述
*/
String getDesc();
}
✅ 所有需要"值+描述"功能的枚举都应实现此接口。
2.3 实现枚举类:SmsProvider
java
public enum SmsProvider implements IOxEnum {
aliyun("aliyun", "阿里云"),
dh3t("dh3t", "大汉三通");
private String value;
private String desc;
SmsProvider(String value, String desc) {
this.value = value;
this.desc = desc;
}
@Override
public String getValue() {
return this.value;
}
@Override
public String getDesc() {
return this.desc;
}
}
✅ 注意:必须实现 getValue()
和 getDesc()
方法。
2.4 配置 MyBatis-Plus 扫描枚举包
在 application.yml
中添加配置,确保 MP 能识别你的枚举:
java
mybatis-plus:
type-enums-package: com.yourcompany.enums # 替换为你的枚举包路径
✅ 这样 MyBatis-Plus 才能在 CRUD 时自动完成枚举转换!
🎛️ 3. 实现自定义 JSON 序列化器:自动注入 Desc
字段
现在进入最关键的一步:通过 JsonSerializer
在序列化时动态添加 xxxDesc
字段。
创建 OxJsonSerialize
序列化器
java
@Slf4j
public class OxJsonSerialize<T> extends JsonSerializer<T> {
@Override
public void serialize(T value, JsonGenerator gen, SerializerProvider serializers)
throws IOException {
String fieldName = gen.getOutputContext().getCurrentName();
log.info("正在序列化字段: {}, 值: {}", fieldName, value);
if (value instanceof IEnum) {
try {
Class<?> clazz = value.getClass();
Field descField = clazz.getDeclaredField("desc");
descField.setAccessible(true);
String desc = (String) descField.get(value);
// 先输出原始字段(如 "provider": "aliyun")
gen.writeObject(value);
// 再输出描述字段(如 "providerDesc": "阿里云")
gen.writeFieldName(fieldName + "Desc");
gen.writeString(desc);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException("枚举字段 'desc' 访问失败,请检查是否正确定义", e);
}
} else {
gen.writeObject(value); // 非枚举原样输出
}
}
}
🔍 原理说明:
- 利用
JsonGenerator
控制输出流程- 获取当前字段名,动态拼接
Desc
后缀- 反射读取
desc
字段内容并写入 JSON
🏷️ 4. 在实体类中使用自定义序列化器
在你的实体类字段上添加 @JsonSerialize
注解:
java
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
@Data
public class SmsTemplate {
private String name;
@JsonSerialize(using = OxJsonSerialize.class)
private SmsProvider provider; // 自动输出 provider + providerDesc
}
✅ 调用接口返回结果如下:
java
{
"name": "验证码模板",
"provider": "aliyun",
"providerDesc": "阿里云"
}
成功实现自动扩展描述字段!
✅ 5. 方案优势总结
优势 | 说明 |
---|---|
免前端维护映射 | 新增枚举后,前端无需修改代码 |
前后端解耦 | 所有逻辑集中在后端,职责清晰 |
复用性强 | 只要实现 IOxEnum 接口,所有枚举均可使用 |
无缝集成 MP | 利用 IEnum 实现数据库自动映射 |
扩展灵活 | 可进一步支持 color 、icon 等元信息输出 |
📌 6. 注意事项 & 建议
- 字段命名规范 :建议所有描述字段统一以
Desc
结尾,便于前端识别。 - 泛型增强 :可将
OxJsonSerialize
改造成支持更多类型(如Integer
枚举)。 - 日志调试:上线初期建议保留日志,便于排查字段匹配问题。
🎉 结语
通过 MyBatis-Plus + IEnum + JsonSerialize
的组合拳,我们实现了:
一次定义枚举,多端自动生效 ✅
再也不用写一堆 if-else
或维护冗长的 Map<String, String>
映射表了!