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
                              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的枚举值已经存进去了,大功告成。
    Java--业务场景:在Spring项目启动时加载Java枚举类到Redis中_第1张图片

你可能感兴趣的:(Spring,java,spring,redis)