Java--业务场景:在Spring项目启动时加载Java枚举类到Redis中
前言
- 新的一年即将来到,回首2023年,也是学习了许多,不断进步。今天带来的是项目中遇到的一个业务要求处理方法总结:项目具有很多的枚举类,而这些枚举类在前端页面中需要作为下拉框选项等组件被前端获取。为了后续获取枚举值更加方便快捷,我们在项目启动的时候将所有Java枚举类用一个hash存入Redis中,在提供一个接口,使得前端可以从Redis获取自己想要的枚举值。下文将讲解实现步骤。
实现项目启动时加载枚举值到Redis
1. 定义EnumInterface接口
- 定义EnumInterface接口,实现该接口的枚举类会在后续被识别,在项目启动时加入redis
1
2
3
4
5
6
7
8
9public interface EnumInterface {
String getCode();
String getDesc();
//枚举类描述 该值后续会与枚举类名拼接 成为Redis hash的key
String enumDesc();
}2. 创建EnumDTO
- 创建EnumDTO,将存放每个枚举类对象,同一个枚举类的所有对象最终会放入同一个列表,作为Redis hash的value。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class EnumDto implements Serializable {
private static final long serialVersionUID = 1L;
private String typeEn;
private String typeCh = "";
private String code;
private String desc;
private String enumName;
}3. 创建ClassUtils工具类
- 该类提供一个方法,通过Java反射获取某个包中所有的枚举类,将它们存入列表。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
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接口,声明有关枚举类操作的接口
1
2
3
4
5
6public interface EnumService {
/**
* 扫描路径下的所有JAVA枚举类,并加载到redis缓存中
*/
void scanAndLoadEnumClassToRedis();
}5. 创建EnumServiceImpl
- 创建EnumService实现类EnumServiceImpl,实现scanAndLoadEnumClassToRedis方法,该方法实现将枚举值加载到Redis中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public class EnumServiceImpl implements EnumService {
private RedisOperation redisOperation;
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方法,给枚举类们加上描述,这个描述很重要,以下是一个例子。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46public 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;
}
public String enumDesc() {
return "操作类型";
}
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被初始化或者更新时发布。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18/**
* 枚举值初始化
* 系统启动的时候,将枚举值全部刷新一遍
*/
public class ApplicationInit implements ApplicationListener<ContextRefreshedEvent> {
private EnumService enumService;
public void onApplicationEvent(ContextRefreshedEvent event) {
log.info("================开始初始化系统数据===========");
log.info("开始加载JAVA枚举值列表");
enumService.scanAndLoadEnumClassToRedis();
log.info("加载枚举值列表完成");
log.info("================初始化系统数据结束===========");
}
}
测试结果
上面的步骤完成后,就可以启动项目了,查看日志结果了:
日志成功打印之后,进入Redis可视化工具,可以看到Redis的枚举值已经存进去了,大功告成。
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.