当前位置: 首页 > news >正文

不用 if-else,Spring Boot 怎么知道 ?status=10 是哪个枚举?

图片

几乎在每个项目中,我们都会定义大量的枚举(Enum)来表示状态、类型等。一个常见的实践是为枚举赋予一个数值或字符串 code,以便在数据库和前后端交互中使用,例如:

public enum OrderStatusEnum {PENDING_PAYMENT(10, "待支付"),PROCESSING(20, "处理中"),SHIPPED(30, "已发货");private final int code;private final String description;// ...
}

但问题来了:当后端 Controller 接收前端传来的参数(如 ?status=10)时,Spring MVC 默认并不知道如何将 10 这个 Integer 自动转换为 OrderStatusEnum.PENDING_PAYMENT。于是,我们的 Controller 代码常常会变成这样:

@GetMapping("/orders")
public List<Order> getOrders(@RequestParam Integer status) {OrderStatusEnum statusEnum = OrderStatusEnum.fromCode(status); // 手动转换if (statusEnum == null) {throw new IllegalArgumentException("Invalid status code");}// ...
}

这种手动转换的代码充满了 if-else 和重复的校验逻辑,非常丑陋。本文将带你构建一个通用的枚举转换 Starter,让你的 Controller 可以直接、优雅地接收枚举类型,彻底告别这些样板代码。

1. 项目设计与核心思路

我们的 enum-converter-starter 目标如下:

  1. 1. 通用性: 无需为每个枚举都写一个转换器,一个 Starter 解决所有问题。

  2. 2. 约定驱动: 只要枚举遵循一个简单的约定(实现一个通用接口),就能被自动识别和转换。

  3. 3. 自动注册: 引入 Starter 依赖后,转换逻辑自动在 Spring MVC 中生效。

核心实现机制:ConverterFactory
Spring 框架提供了一个 ConverterFactory<S, R> 接口。它是一个能创建 Converter<S, T extends R> 实例的工厂。我们可以创建一个 ConverterFactory<String, Enum>,它能为任何 Enum 类型的子类 T 创建一个从 String 到 T 的转换器。

实现流程:

  1. 1. 定义一个通用接口,如 BaseEnum,它包含一个 getCode() 方法。

  2. 2. 所有需要被自动转换的枚举都实现 BaseEnum 接口。

  3. 3. 创建一个 StringToEnumConverterFactory,它会为所有实现了 BaseEnum 接口的枚举,生成一个能根据 getCode() 的值进行匹配的转换器。

  4. 4. 通过 WebMvcConfigurer 将这个 ConverterFactory 注册到 Spring 的格式化服务中。

2. 创建 Starter 项目与核心组件

我们采用 autoconfigure + starter 的双模块结构。

步骤 2.1: 依赖 (autoconfigure 模块)

这个 Starter 非常轻量,核心依赖只需要 spring-boot-starter-web

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency></dependencies>
步骤 2.2: 定义约定接口和通用转换工厂

BaseEnum (约定接口):

package com.example.converter.autoconfigure.core;public interface BaseEnum {/*** 获取枚举的代码值* @return code 值 (可以是 Integer, String 等)*/Object getCode();
}

StringToEnumConverterFactory (核心转换逻辑):

package com.example.converter.autoconfigure.core;import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;public class StringToEnumConverterFactory implements ConverterFactory<String, Enum<?>> {@Overridepublic <T extends Enum<?>> Converter<String, T> getConverter(Class<T> targetType) {// 我们只处理实现了 BaseEnum 接口的枚举if (!BaseEnum.class.isAssignableFrom(targetType)) {// 对于未实现接口的枚举,使用 Spring 默认的转换器 (按名称匹配)return new StringToEnumConverter(targetType);}return new StringToBaseEnumConverter<>(targetType);}// 内部类,负责将 String 转换为实现了 BaseEnum 的枚举private static class StringToBaseEnumConverter<T extends Enum<?>> implements Converter<String, T> {private final Class<T> enumType;StringToBaseEnumConverter(Class<T> enumType) {this.enumType = enumType;}@Overridepublic T convert(String source) {if (source.isEmpty()) {return null;}for (T enumConstant : enumType.getEnumConstants()) {if (enumConstant instanceof BaseEnum) {// 使用 getCode() 的值进行比较if (String.valueOf(((BaseEnum) enumConstant).getCode()).equals(source)) {return enumConstant;}}}return null; // or throw exception}}// 内部类,用于兼容 Spring 默认的按名称转换private static class StringToEnumConverter<T extends Enum> implements Converter<String, T> {private final Class<T> enumType;public StringToEnumConverter(Class<T> enumType) {this.enumType = enumType;}@Overridepublic T convert(String source) {if (source.isEmpty()) {return null;}return (T) Enum.valueOf(this.enumType, source.trim());}}
}

3. 自动装配的魔法 (EnumConverterAutoConfiguration)

步骤 3.1: 配置属性类
@ConfigurationProperties(prefix = "enum.converter")
public class EnumConverterProperties {private boolean enabled = true; // 默认开启// Getters and Setters...
}
步骤 3.2: 自动配置主类

这个类负责将我们的 ConverterFactory 注册到 Spring MVC。

package com.example.converter.autoconfigure;import com.example.converter.autoconfigure.core.StringToEnumConverterFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
@EnableConfigurationProperties(EnumConverterProperties.class)
@ConditionalOnProperty(prefix = "enum.converter", name = "enabled", havingValue = "true", matchIfMissing = true)
public class EnumConverterAutoConfiguration implements WebMvcConfigurer {@Overridepublic void addFormatters(FormatterRegistry registry) {// 将我们的通用转换工厂注册进去registry.addConverterFactory(new StringToEnumConverterFactory());}
}
步骤 3.3: 注册自动配置

在 autoconfigure 模块的 resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中添加:

com.example.converter.autoconfigure.EnumConverterAutoConfiguration

4. 如何使用我们的 Starter

步骤 4.1: 引入 Starter 依赖

<dependency><groupId>com.example</groupId><artifactId>enum-converter-spring-boot-starter</artifactId><version>1.0.0</version>
</dependency>

步骤 4.2: 让你的枚举实现约定接口

import com.example.converter.autoconfigure.core.BaseEnum;public enum OrderStatusEnum implements BaseEnum {PENDING_PAYMENT(10, "待支付"),PROCESSING(20, "处理中"),SHIPPED(30, "已发货");private final Integer code;private final String description;OrderStatusEnum(Integer code, String description) {this.code = code;this.description = description;}@Overridepublic Integer getCode() {return this.code;}
}

步骤 4.3: 在 Controller 中直接接收枚举类型
现在,你的 Controller 可以写得无比清爽:

改造前 (丑陋):

// @GetMapping("/orders")
// public List<Order> getOrdersByStatusCode(@RequestParam Integer status) {
//     OrderStatusEnum statusEnum = // ... 手动 if-else 或 switch 转换
//     return orderService.findByStatus(statusEnum);
// }

改造后 (优雅):

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
public class OrderController {@GetMapping("/orders")public String getOrdersByStatus(@RequestParam OrderStatusEnum status) {// Spring MVC 已经自动将请求参数 "10" 转换为了 OrderStatusEnum.PENDING_PAYMENTSystem.out.println("查询状态为: " + status.name());return "查询成功,状态为: " + status;}
}

验证:

  • • 访问 http://localhost:8080/orders?status=20

  • • 控制台将打印 查询状态为: PROCESSING

  • • 浏览器将收到 查询成功,状态为: PROCESSING

总结

通过自定义一个 Spring Boot Starter 和巧妙地利用 ConverterFactory,我们将繁琐、重复的枚举转换逻辑从业务代码中彻底剥离。这不仅让 Controller 层代码变得更加简洁、类型安全,还通过一个统一的 BaseEnum 接口,在团队内部推行了一套优雅的枚举设计规范。

这个看似小巧的 Starter,是提升代码质量和“开发幸福感”的一大利器,是每一个追求代码洁癖的团队都值得拥有的基础组件。

http://www.xdnf.cn/news/1353601.html

相关文章:

  • 小白成长之路-k8s原理(一)
  • STM32学习笔记19-FLASH
  • [Mysql数据库] 选择备份策略选择题
  • 工业场景烟雾识别误报率↓82%!陌讯多模态融合算法实战解析
  • 水泉村信息化服务小程序的设计与实验
  • 54 C++ 现代C++编程艺术3-移动构造函数
  • 用 Go + GitHub Models API 打造一个免费的 ChatBot
  • 全面解析JVM预热:原理、价值与实践指南
  • MYSQL-约束
  • 【数据结构】线性表——链表
  • 微服务的编程测评系统15-头像上传-OSS
  • 高阶数据结构---ST表
  • kafaka知识要点
  • VLOOKUP专题训练
  • UE C++ 堆化
  • windows中bat脚本的一些操作(三)
  • 算法第五十五天:图论part05(第十一章)
  • 图论与最短路学习笔记
  • 【数据结构】跳表的概率模型详解与其 C 代码实现
  • 深度学习开篇
  • `strlen` 字符串长度函数
  • python 字典有序性的实现和OrderedDict
  • 计算机网络 各版本TLS握手的详细过程
  • 电脑零广告快响应提速(一)之卸载搜狗输入法使用RIME—东方仙盟
  • python re模块常用方法
  • MySQL详细介绍指南
  • 蓝牙aoa仓库管理系统功能介绍
  • [e3nn] 归一化 | BatchNorm normalize2mom
  • 【技术突破】动态目标误检率↓83.5%!陌讯多模态融合算法在智慧城管的实战优化
  • 基于电力电子变压器的高压脉冲电源方案复现