文章目录
- 前言
- 实现项目启动时加载枚举值到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的枚举值已经存进去了,大功告成。