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

如何统一管理枚举类?

Hello,大家好,今天我们来聊一下关于系统中的枚举是如何统一进行管理的。

业务场景

我们公司有这样的一个业务场景前端表单中 下拉选择的枚举值,是需要从后端获取的。那么这时候有个问题,我们不可能每次新增加一个枚举,都需要 改造获取枚举 的相关接口(getEnum),所以我们就需要对系统中的所有枚举类,进行统一的一个管理。

核心思路

为了解决这个问题,我们采用了如下的方案

  1. 当服务启动时,统一对 枚举类 进行 注册发现
  2. 枚举管理类,对外暴露一个方法,可以根据我的key 去获取对应的枚举值

相关实现

枚举定义

基于以上的思想,我们对枚举类定义了如下的规范,例如

@**IsEnum**
public enum BooleanEnum implements BaseEnums {
    YES("是", "1"),
    NO("否", "2")
            ;

    private String text;
    @EnumValue
    private String value;

    YesNoEnum(String text, String value) {
        this.text = text;
        this.value = value;
    }

    public String getText() {
        return text;
    }

    @Override
    public String getValue() {
        return value;
    }

    @Override
    public String toString() {
        return "YesNoEnum{" +
                "text='" + text + '\'' +
                ", value=" + value +
                '}';
    }

    @Override
    public EnumRequest toJson() {
        return new EnumRequest(value, text);
    }

    @JsonCreator
    public static YesNoEnum fromCode(String value) {
        for (YesNoEnum status : YesNoEnum.values()) {
            if (status.value.equals(value)) {
                return status;
            }
        }
        return null; // 或抛出异常
    }
}
  1. 所有枚举均使用 @IsEnum进行标记(这是一个自定义注解)
  2. 所有枚举均实现 BaseEnums 接口(具体作用后续会提到)
  3. 所有的枚举的 value 值 统一使用 String 类型,并使用 @EnumValue 注解进行标记
    1. 这主要是为了兼容Mybatis Plus 查表时 基础数据类型与枚举类型的转化
    2. 如果将 value 定义为Object类型,Mybatis Plus 无法正确识别,会报错
  4. 使用 @JsonCreator 注解标记转化函数
    1. 这个注解是有Jackson提供的,使用了从 基础数据类型到枚举类型的转化

注册发现

那么我们是如何实现服务的注册发现的呢?在这里主要是 使用了 SpringBoot 提供的接口CommandLineRunner (关于CommandLineRunner 可以参考这篇文章https://blog.csdn.net/python113/article/details/109244712)

在应用启动时,我们回去扫描 枚举所在的 包,通过 类上 是否包含 IsEnum 注解,判断是否需要对外暴露

 // 指定要扫描的包
        String basePackage = "com.xxx.enums";

        // 创建扫描器
        ClassPathScanningCandidateComponentProvider scanner =
                new ClassPathScanningCandidateComponentProvider(false);
        scanner.addIncludeFilter(new AnnotationTypeFilter(EnumMarker.class));

        // 扫描指定包
        Set<BeanDefinition> beans = scanner.findCandidateComponents(basePackage);

        // 注册枚举
        for (org.springframework.beans.factory.config.BeanDefinition beanDefinition : beans) {
            try {
                Class<?> enumClass = Class.forName(beanDefinition.getBeanClassName());
                if (Enum.class.isAssignableFrom(enumClass)) {
                    enumManager.registerEnum((Class<? extends Enum<?>>) enumClass);
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }

ClassPathScanningCandidateComponentProvider 是 Spring 框架中的一个类,主要用于扫描指定路径下符合条件的候选组件(通常是 Bean 定义)。它允许我们在类路径中扫描并筛选符合特定条件(如标注特定注解、实现某接口等)的类,以实现自动化配置、依赖注入等功能。

典型用途

在基于注解的 Spring 应用中,我们可以使用它来动态扫描特定包路径下的类并注册成 Spring Bean。例如,Spring 扫描 @Component@Service@Repository 等注解标注的类时就会用到它。

主要方法

  • addIncludeFilter(TypeFilter filter):添加一个包含过滤器,用于筛选扫描过程中包含的类。
  • findCandidateComponents(String basePackage):扫描指定包路径下的候选组件(符合条件的类),并返回符合条件的 BeanDefinition 对象集合。
  • addExcludeFilter(TypeFilter filter):添加一个排除过滤器,用于排除特定类。

最终呢,会将找到的枚举值,放在一个EnumManager中的一个Map集合中

 private final Map<Class<?>, List<Enum<?>>> enumMap = new HashMap<>(); // 类与枚举类型的映射关系
 private final Map<String, List<Enum<?>>> enumNameMap = new HashMap<>(); // 名称与枚举的映射管理
 public <E extends Enum<E>> void registerEnum(Class<? extends Enum<?>> enumClass) {
        Enum<?>[] enumConstants = enumClass.getEnumConstants();
        final List<Enum<?>> list = Arrays.asList(enumConstants);
        enumMap.put(enumClass, list);
        enumNameMap.put(enumClass.getSimpleName(), list); 
    }

enumClass.getEnumConstants() 是 Java 反射 API 中的一个方法,用于获取某个枚举类中定义的所有枚举实例。getEnumConstants() 会返回一个包含所有枚举常量的数组,每个元素都是该枚举类的一个实例。

这样子我们就可以通过枚举的名称或者class 获取枚举列表返回给前端

enumMap.get(enumClass); enumNameMap.get(enumName);

请求与响应

我们项目中使用的序列化器 是Jackson,通过 @JsonValue@JsonCreator两个注解实现的。

@JsonValue:对象序列化为json时会调用这个注解标记的方法

@JsonValue
public EnumRequest toJson() {
        return new EnumRequest(value, text);
    }

@JsonCreator :json反序列化对象时会调用这个注解标记的方法

 @JsonCreator
    public static YesNoEnum fromCode(String value) {
        for (YesNoEnum status : YesNoEnum.values()) {
            if (status.value.equals(value)) {
                return status;
            }
        }
        return null; // 或抛出异常
    }

但是这里有个坑,我们SpringBoot的版本是2.5,使用 @JsonCreator 时会报错,这时候只需要降低jackson 的版本就可以了

 // 排除 spring-boot-starter-web 中的jsckson
 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>com.fasterxml.jackson.core</groupId>
                    <artifactId>jackson-databind</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>com.fasterxml.jackson.core</groupId>
                    <artifactId>jackson-core</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>com.fasterxml.jackson.core</groupId>
                    <artifactId>jackson-annotations</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        
   <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.10.5</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.10.5</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.10.5</version>
        </dependency>      

Mybatis Plus 枚举中的使用

  • 在applicaton.yml文件中添加如下参数
# 在控制台输出sql
mybatis-plus:
  type-enums-package: com.xxx.enums // 定义枚举的扫描包
  • 将枚举值中 存入到数据库的字段 使用 @EnumValue注解进行标记,例如:上面提供的YesNoEnum 类中的value字段使用 @EnumValue 进行了标记 数据库中实际保存的就是 value 的值(1/2)
  • 将domian 中 直接定义为枚举类型就可以了,是不是非常简单呢?

以上就是本篇文章的全部内容,如果有更好的方案欢迎小伙伴们评论区讨论!


http://www.kler.cn/a/374016.html

相关文章:

  • linux 网络包接收过程
  • 17. 云计算和分布式计算
  • SpringBoot 下的Excel文件损坏与内容乱码问题
  • 【MySQL】深入理解隔离性
  • 常见的机器学习模型汇总
  • Unity之如何在Linux上部署Dedicated Server专用服务器
  • ASPICE 4.0引领自动驾驶未来:机器学习模型的特点与实践
  • JS面试八股文(三)
  • 四足机器人实战篇之三:四足机器人嵌入式硬件设计
  • 013:无人机航线规划的概念
  • 华为OD机试真题---获得完美走位
  • 细说 ThreadPool(线程池)使用与优势以及实现方式
  • 微软官方 .NET 混淆软件 Dotfuscator
  • Nginx 网关解决 Geoserver 图层访问控制
  • idm扩展自动更新,导致不能正常使用处理方法
  • 企业应该采用和支持网络安全的几个实践
  • 高考相关 APP 案例分享
  • 【安全性分析】正式安全分析与非正式安全分析
  • 使用API有效率地管理Dynadot域名,将域名移动至某一文件夹中
  • 【瑞吉外卖】-day03
  • Qt 实战(10)模型视图 | 10.5、代理
  • 音视频入门基础:FLV专题(21)——FFmpeg源码中,获取FLV文件音频信息的实现(上)
  • 组件封装思路
  • JDBC——获取DBMS连接、优化与工具类封装
  • 深入 Prometheus 监控生态 - 第六篇:与 Grafana 实现系统全面监控(健康状态和任务状态看板)
  • 邮件发送excel带预览excel功能