Java--业务场景:在Spring项目启动时加载Java枚举类到Redis中

文章目录

        • 前言
        • 实现项目启动时加载枚举值到Redis
          • 1. 定义EnumInterface接口
          • 2. 创建EnumDTO
          • 3. 创建ClassUtils工具类
          • 4. 创建EnumService接口
          • 5. 创建EnumServiceImpl
          • 6. 修改枚举类
          • 7. 创建ApplicationInit
        • 测试结果

前言
  • 新的一年即将来到,回首2023年,也是学习了许多,不断进步。今天带来的是项目中遇到的一个业务要求处理方法总结:项目具有很多的枚举类,而这些枚举类在前端页面中需要作为下拉框选项等组件被前端获取。为了后续获取枚举值更加方便快捷,我们在项目启动的时候将所有Java枚举类用一个hash存入Redis中,在提供一个接口,使得前端可以从Redis获取自己想要的枚举值。下文将讲解实现步骤。
实现项目启动时加载枚举值到Redis
1. 定义EnumInterface接口
  • 定义EnumInterface接口,实现该接口的枚举类会在后续被识别,在项目启动时加入redis
    public interface EnumInterface {
    
        String getCode();
    
        String getDesc();
     
        //枚举类描述 该值后续会与枚举类名拼接 成为Redis hash的key  
        String enumDesc();
      }
    
2. 创建EnumDTO
  • 创建EnumDTO,将存放每个枚举类对象,同一个枚举类的所有对象最终会放入同一个列表,作为Redis hash的value。
      @Data
      @ApiModel(value = "EnumDto", description = "【枚举值-枚举值DTO】")
      public class EnumDto implements Serializable {
    
          private static final long serialVersionUID = 1L;
      
          @ApiModelProperty(value = "枚举值类型-对应枚举值类名")
          private String typeEn;
      
          @ApiModelProperty(value = "枚举值类型-对应枚举值类名的desc描述信息")
          private String typeCh = "";
    
          @ApiModelProperty(value = "枚举值类型-对应枚举值的code值")
          private String code;
    
          @ApiModelProperty(value = "枚举值类型-对应枚举值的desc值")
          private String desc;
      
          @ApiModelProperty(value = "枚举值类型-对应枚举实例的名字")
          private String enumName;
    
      }
    
3. 创建ClassUtils工具类
  • 该类提供一个方法,通过Java反射获取某个包中所有的枚举类,将它们存入列表。
      @Slf4j
      public class ClassUtils {
          //为了方便管理,我们将枚举类放在同一个包下,这里写入自己项目中枚举类所在的包名。
          private static final String PACKAGE_NAME = "com.common.base.enums";
      
          public static List<Class<Enum>> getAllEnumClasses() throws ClassNotFoundException, IOException {
              List<Class<Enum>> list = new ArrayList<>();
              for (String className : getClassName(PACKAGE_NAME, true)) {
                  Class<?> clazz = Class.forName(className);
                  if (Enum.class.isAssignableFrom(clazz) && EnumInterface.class.isAssignableFrom(clazz)) {
                      list.add((Class<Enum>) clazz);
                  }
              }
              return list;
          }
          /**
           * 获取某包下的所有类
           * @param packageName  包名
           * @param childPackage 是否遍历子包
           */
          public static List<String> getClassName(String packageName, boolean childPackage) throws IOException {
              List<String> fileNames = null;
              ClassLoader loader = Thread.currentThread().getContextClassLoader();
              String packagePath = packageName.replace(".", "/");
              URL url = loader.getResource(packagePath);
              if (url != null) {
                  String type = url.getProtocol();
                  if (type.equals("file")) {
                      fileNames = getClassNameByFile(url.getPath(), childPackage);
                  }
              } else {
                  fileNames = getClassNameByJars(((URLClassLoader) loader).getURLs(), packagePath, childPackage);
              }
              return fileNames;
          }
          /**
           * 从项目文件获取某包下所有类
           *
           */
          private static List<String> getClassNameByFile(String filePath, boolean childPackage) throws UnsupportedEncodingException {
              List<String> myClassName = new ArrayList<>();
              // 解决路径包含中文的情况
              filePath = java.net.URLDecoder.decode(filePath,"utf-8");
              File file = new File(filePath);
              boolean exists = file.exists();
              File[] childFiles = file.listFiles();
              assert childFiles != null;
              for (File childFile : childFiles) {
                  if (childFile.isDirectory()) {
                      if (childPackage) {
                          myClassName.addAll(getClassNameByFile(childFile.getPath(), childPackage));
                      }
                  } else {
                      String childFilePath = childFile.getPath();
                      if (childFilePath.endsWith(".class")) {
                          childFilePath = childFilePath.substring(childFilePath.indexOf("\\classes") + 9,
                                  childFilePath.lastIndexOf("."));
                          childFilePath = childFilePath.replace("\\", ".");
                          myClassName.add(childFilePath);
                      }
                  }
              }
              return myClassName;
          }
      }
    
4. 创建EnumService接口
  • 创建EnumService接口,声明有关枚举类操作的接口
      public interface EnumService {
          /**
           * 扫描路径下的所有JAVA枚举类,并加载到redis缓存中
           */
          void scanAndLoadEnumClassToRedis();
      }
    
5. 创建EnumServiceImpl
  • 创建EnumService实现类EnumServiceImpl,实现scanAndLoadEnumClassToRedis方法,该方法实现将枚举值加载到Redis中
      @Service
      @Slf4j
      public class EnumServiceImpl implements EnumService {   
          @Autowired
          private RedisOperation redisOperation;
          @Override
          public void scanAndLoadEnumClassToRedis() {
              try {
                  //声明Redis中存放的数据结构
                  Map<String, List<EnumDto>> map = new HashMap<>();
                  //先删除原来的枚举类key,RedisKeyConstant.SYSTEM_ENUMS_CACHE_KEY 为存放缓存的key字符串,在常量类中自己定义
                  redisOperation.del(RedisKeyConstant.SYSTEM_ENUMS_CACHE_KEY);
                  //通过ClassUtils得到枚举类名,反射得到属性,填充属性到EnumDto中
                  ClassUtils.getAllEnumClasses().forEach(enumClass -> {
                      try {
                          EnumInterface[] enumInterfaces = (EnumInterface[]) enumClass.getMethod("values").invoke(new Object());
                          Arrays.stream(enumInterfaces).forEach(enumInterface -> {
                              EnumDto enumDto = new EnumDto();
                              enumDto.setCode(enumInterface.getCode());
                              enumDto.setTypeEn(enumClass.getName());
                              enumDto.setTypeCh(enumInterface.enumDesc());
                              enumDto.setDesc(enumInterface.getDesc());
                              enumDto.setEnumName(((Enum) enumInterface).name());
                              //hash的key:枚举值类名|枚举类描述信息
                              //hash的value:List<EnumDto>
                              String key = enumClass.getName() + "|" + enumInterface.enumDesc();
                              if (null == map.get(key)) {
                                  List<EnumDto> list = new ArrayList<>();
                                  list.add(enumDto);
                                  map.put(key, list);
                              } else {
                                  map.get(key).add(enumDto);
                              }
                          });
                      } catch (Exception e) {
                          log.error("加载JAVA枚举值失败", e);
                          redisOperation.del(RedisKeyConstant.SYSTEM_ENUMS_CACHE_KEY);
                          throw new ProcessException(CommonConstants.ENUM_PROCESSING_EXCEPTION, "加载JAVA枚举值到Reids中发生错误,请检查代码!");
                      }
                  });
                  map.forEach((key, val) -> 
                      //存入Redis
                      redisOperation.hset(RedisKeyConstant.SYSTEM_ENUMS_CACHE_KEY, key, val)
                  );
              } catch (Exception e) {
                  log.error("加载JAVA枚举值失败", e);
                  throw new ProcessException(CommonConstants.ENUM_PROCESSING_EXCEPTION, "加载JAVA枚举值到Reids中发生错误,请检查代码!");
              }
          }
      }
    
6. 修改枚举类
  • 让需要加载到Redis中的枚举类实现EnumInterface接口,记得重写enumDesc方法,给枚举类们加上描述,这个描述很重要,以下是一个例子。
      public enum OperationTypeEnum implements EnumInterface {
          UPDATE("update", "更新"),
          DELETE("delete", "删除"),
          ADD("add","增加"),
          GET("show","展示");
    
          private final String code;
          private final String desc;
    
          OperationTypeEnum(String code, String desc) {
            this.code = code;
            this.desc = desc;
          }
    
          public String getCode() {
            return code;
          }
        
          @Override
          public String enumDesc() {
            return "操作类型";
          }
    
          @Override
          public String getDesc() {
            return this.desc;
          }
        
          public static String getDesc(String code) {
            for (OperationTypeEnum publishEnum : OperationTypeEnum.values()) {
              if (publishEnum.getCode().equals(code)) {
                return publishEnum.getDesc();
              }
            }
            return null;
          }
    
          public static OperationTypeEnum getByCode(String code) {
            for (OperationTypeEnum examSourceTypeEnum : OperationTypeEnum.values()) {
              if (examSourceTypeEnum.getCode().equals(code)) {
                return examSourceTypeEnum;
              }
            }
            return null;
          }
      }
    
7. 创建ApplicationInit
  • ContextRefreshedEvent是Spring内置的上下文更新事件,该事件会在ApplicationContext被初始化或者更新时发布。
      /**
       * 枚举值初始化
       * 系统启动的时候,将枚举值全部刷新一遍
       */
       @Slf4j
       @Component
       public class ApplicationInit implements ApplicationListener<ContextRefreshedEvent> {
           @Autowired
           private EnumService enumService;
           @Override
           public void onApplicationEvent(ContextRefreshedEvent event) {
              log.info("================开始初始化系统数据===========");
              log.info("开始加载JAVA枚举值列表");
              enumService.scanAndLoadEnumClassToRedis();
              log.info("加载枚举值列表完成");
              log.info("================初始化系统数据结束===========");
           }
       }
    
测试结果
  • 上面的步骤完成后,就可以启动项目了,查看日志结果了:
    在这里插入图片描述

  • 日志成功打印之后,进入Redis可视化工具,可以看到Redis的枚举值已经存进去了,大功告成。
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/326205.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深入解析JavaScript中new Function语法

&#x1f9d1;‍&#x1f393; 个人主页&#xff1a;《爱蹦跶的大A阿》 &#x1f525;当前正在更新专栏&#xff1a;《VUE》 、《JavaScript保姆级教程》、《krpano》、《krpano中文文档》 ​ ​ ✨ 前言 Function是JavaScript中非常重要的内置构造函数,可以用来动态创建函数…

Redis和MySQL如何保持数据一致性

前言 在高并发的场景下&#xff0c;大量的请求直接访问Mysql很容易造成性能问题。所以&#xff0c;我们都会用Redis来做数据的缓存&#xff0c;削减对数据库的请求。但是&#xff0c;Mysql和Redis是两种不同的数据库&#xff0c;如何保证不同数据库之间数据的一致性就非常关键…

Pyside6/PyQt6图标设置必备:窗口图标、软件图标、任务栏图标与系统托盘图标设置详解(含示例源码)

文章目录 📖 介绍 📖🏡 环境 🏡📒 图标设置 📒📝 设置窗口图标📝 设置软件/任务栏图标📝 设置系统托盘图标🎈 添加右键菜单/全局快捷键/隐藏任务栏图标⚓️ 相关链接 ⚓️📖 介绍 📖 在创建图形用户界面(GUI)应用程序时,一个吸引人的图标是必不可少…

day10

1.构造代码块和构造方法的区别 &#xff5b;代码块 &#xff5d; public 类名 () {} 都是实例化一个对象的时候执行的 只不过构造代码块先于构造方法执行的 2.局部变量和成员变量区别局部变量写在方法中&#xff0c;只能在方法体中使用&#xff0c;出了这个方法就不能再使用了成…

AI对决:ChatGPT与文心一言的深度比较

. 个人主页&#xff1a;晓风飞 专栏&#xff1a;数据结构|Linux|C语言 路漫漫其修远兮&#xff0c;吾将上下而求索 文章目录 引言ChatGPT与文心一言的比较Chatgpt的看法文心一言的看法Copilot的观点chatgpt4.0的回答 模型的自我评价自我评价 ChatGPT的优势在这里插入图片描述 文…

【刷题】leetcode 1 . 两数之和

两数之和 两数之和1 思路一 &#xff08;简单突破&#xff09;2 思路二 &#xff08;进行优化&#xff09;3 思路三 &#xff08;哈希表 我还不会&#xff09; 谢谢阅读Thanks♪(&#xff65;ω&#xff65;)&#xff89;下一篇文章见&#xff01;&#xff01;&#xff01; 两数…

uniapp的nvue是什么

什么是nvue uni-app App 端内置了一个基于 weex 改进的原生渲染引擎&#xff0c;提供了原生渲染能力。 在 App 端&#xff0c;如果使用 vue 页面&#xff0c;则使用 webview 渲染&#xff1b;如果使用 nvue 页面(native vue 的缩写)&#xff0c;则使用原生渲染。一个 App 中可…

USB Redirector本地安装并结合内网穿透实现远程共享和访问USB设备

文章目录 前言1. 安装下载软件1.1 内网安装使用USB Redirector1.2 下载安装cpolar内网穿透 2. 完成USB Redirector服务端和客户端映射连接3. 设置固定的公网地址 前言 USB Redirector是一款方便易用的USB设备共享服务应用程序&#xff0c;它提供了共享和访问本地或互联网上的U…

十五.流程控制与游标

流程控制与游标 1.流程控制1.1分支结构之IF1.2分支结构值CASE1.3循环结构之LOOP1.4循环结构之WHILE1.5循环结构之REPEAT1.6跳转语句之LEAVE语句1.7跳转语句之ITERATE语句 2.游标2.1什么是游标2.2使用游标步骤4.3举例4.5小结 1.流程控制 解决复杂问题不可能通过一个 SQL 语句完…

深度学习基础知识整理

自动编码器 Auto-encoders是一种人工神经网络&#xff0c;用于学习未标记数据的有效编码。它由两个部分组成&#xff1a;编码器和解码器。编码器将输入数据转换为一种更紧凑的表示形式&#xff0c;而解码器则将该表示形式转换回原始数据。这种方法可以用于降维&#xff0c;去噪…

【图形学】直线光栅化算法(DDA算法和Bresenham算法)

在数学上,直线就是由无穷多个点组成的, 在计算机屏幕显示的话, 需要做一些处理,对于光栅显示器&#xff0c;就是用有限多个点去逼近直线, 我们需要知道每一个像素点的坐标(都是整数) 数学上直线的方程如下 y k x b ykxb ykxb&#xff0c;给定直线的起点坐标 P 0 ( x 0 , y…

开源 UI 组件库和开发工具库概览 | 开源专题 No.59

ant-design/ant-design Stars: 87.9k License: MIT Ant Design 是一个企业级 UI 设计语言和 React UI 库。 为 Web 应用程序设计的企业级 UI。提供一套高质量的开箱即用的 React 组件。使用可预测静态类型编写 TypeScript 代码。包含完整的设计资源和开发工具包。支持数十种语…

QT上位机开发(软件的发布和部署)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 我们在读书的时候&#xff0c;如果程序写好了&#xff0c;这个时候一般直接把exe拷贝给老师就可以了。这就是最原始的软件发布。但是&#xff0c;这…

“核弹级“攻击队视角下的监管痛点解决方案

痛点分析及解决方案 一、辖区企业资产分散且不透明 - 传统的监管体系中&#xff0c;政府监管单位往往面临着辖区企业资产分散且不透明的问题。 - 企业无法梳理自身资产&#xff0c;上报的资产台账无法涵盖全部自身资产 - 监管单位精力有限&#xff0c;无法保证辖区企业资产台账…

解决jmeter测试计划无法保存、另存为的问题

问题&#xff1a; 在保存测试计划时直接保存在C:\Windows\System32&#xff0c; 导致执行时报错Couldn’t save test plan to file&#xff1a;C:\Windows\System32 解决方案&#xff1a; 将路径改为 options--------Look And Feel-------windows

Express框架使用全流程

1.目的和使用场景 对于像我这样不常使用 Node.js 进行开发的人来说&#xff0c;每次开始一个新项目都意味着从头开始设置环境&#xff0c;这个过程相当繁琐。因此&#xff0c;我决定自己构建一个开箱即用的项目脚手架。我的目标是创建一个简单易用的基础框架&#xff0c;能让我…

NET Core发布 HTTP Error 500.31 - Failed to load ASP.NET Core runtime

记录一下踩过的坑&#xff1a; 首先&#xff0c;不论是500.31还是500.30 &#xff0c;首先确保安装了三个文件 1.NET Core RunTime 2.NET SDK 3.NET Hosting 其次&#xff0c;确保三个文件的版本一致&#xff0c;如下&#xff1a; 要装就统一装同一个大版本&#xff0c;不要东…

51单片机学习总结(自学)

1、模块化编程 c语言模块化编程实现思路设计代码 具体的程序实现代码如下所示 1&#xff1a;程序的头文件 2&#xff1a;程序的函数文件 3&#xff1a;程序的主文件控制函数的实现 持续更新中......

训练YOLOS-S

文章目录 1 数据处理2 配置训练参数3 可能会遇到的报错 1 数据处理 修改类别数&#xff1a;在models/detector.py中定位到def build(args):&#xff0c;将num_classes进行修改&#xff0c;改为最大的类别id1。我有4个类别&#xff0c;类别id是从0~3&#xff0c;因此max_id3&am…

怎样才能找到合适的产品说明书模板 方法献上

制作一份专业而吸引人的产品手册对于企业来说至关重要。然而&#xff0c;对于许多企业和个人而言&#xff0c;制作产品手册可能是一个挑战&#xff0c;因为需要一定的设计和排版能力。为了帮助大家更轻松地制作出优质的产品手册&#xff0c;下面将向大家推荐三款优秀的产品手册…