前言
在学习Spring源码的过程中看到,在ClassPathBeanDefinitionScanner中看到spring通过扫描给定的包路径,获取到对应的class资源并判断是否生成BeanDefinition注册到IOC容器中,在这里也进行写了一个Demo记录下学习。
在我们开发的过程中,为了解决硬编码问题我们通常都会把一些类型的数据进行封装成字典然后存入数据库,这的确是一个好的办法,可以规范我们的开发,也大大减少了后期修改维护的成本。 但是也存在此类的问题,我们在开发中也常常会使用到这些类型,那如果我们每次都通过数据库去查询这些字典,在效率上或多或少会有折扣。本篇文章算是改善了一点此类问题吧,通过Spirng包扫描将枚举封装成字典的形式可供接口调用。但是也遗留下了问题:字典需要维护的时候我们需要去动用代码才能完成操作这里希望各位朋友可以提供一些思路解决这个问题。
一、定义测试枚举类public enum TypeEnum {
自动(1),
手动(2);
TypeEnum(Integer value){
this.value = value;
}
private Integer value;
public Integer getValue() {
return value;
}
public void setValue(Integer value) {
this.value = value;
}
//此处一定要重写toString方法,来规定返回格式
@Override
public String toString() {
return new StringBuffer("{\"dictValue\":").append(getValue())
.append(",\"dictName\":\"").append(name())
.append("\"}").toString();
}
}
二、定义枚举配置类
public class EnumDictConfig {
//用于存放枚举的MAP
public static final Map ENUM_MAP = new HashMap();
/**
* 根据key获取枚举类Class
* @param code 处理后的枚举类名,处理方式是将枚举类名首字母转小写,去掉末尾的Enum
* @return
*/
public static Class getEnumClass(String code) {
return ENUM_MAP.get(code);
}
}
三、定义扫描类
*这里是主要代码
public class EnumScanParser {
//通同匹配
private static final String RESOURCE_PATTERN = "/**/*.class";
private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
public void parse(String basePackage) {
if (StringUtils.hasText(basePackage)) {
try {
//根据classname生成class对应的资源路径,需要扫描的包路径
String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(basePackage)
+ RESOURCE_PATTERN;
//获取classname的IO流资源
Resource[] resources = resourcePatternResolver.getResources(pattern);
//MetadataReaderFactory接口 ,MetadataReader的工厂接口。允许缓存每个MetadataReader的元数据集
MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver);
for (Resource resource : resources) {
if (resource.isReadable()) {
//通过class资源(resource)生成MetadataReader
MetadataReader reader = readerFactory.getMetadataReader(resource);
//获取class名
String className = reader.getClassMetadata().getClassName();
Class clz = Class.forName(className);
if(!clz.isEnum()) {
continue;
}
//将枚举类名首字母转小写,去掉末尾的Enum
EnumDictConfig.ENUM_MAP.put(clz.getSimpleName().substring(0, 1).toLowerCase() + clz.getSimpleName().substring(1).replace("Enum", ""), clz);
System.err.println(EnumDictConfig.ENUM_MAP);
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
四、启动进行扫描
在此处进行扫描,与springboot应用启动同时进行。
public class TaskApplication {
private static final String BASE_PACKAGES = "com.test.task.enums";
public static void main(String[] args) {
SpringApplication.run(TaskApplication .class, args);
new EnumScanParser().parse(BASE_PACKAGES);
}
}
五、定义接口
@RestController
@RequestMapping("/dict")
public class TestController {
@GetMapping(value = "/enumDict")
public R findEnumDict(String code) {
try {
//根据key值获取枚举Class
Class clz = EnumDictConfig.getEnumClass(code);
if(clz == null) {
return R.failed("未找到枚举类,请确认参数是否正确");
}
//通过反射调用枚举的values方法
Method valuesMethod = clz.getMethod("values");
Object[] values = (Object[])valuesMethod.invoke(null);
//返回字典格式,一定要重写enum的toString方法,按照系统要求的格式{"dictKey": xxx, "dictValue": xxx}
return R.ok(JSON.parseArray(Arrays.toString(values)));
} catch (Exception e) {
e.printStackTrace();
return R.failed(e.getMessage());
}
}
}
六、测试
这样我们就实现了这个操作,能以数据字典的形式返回给接口,我们在项目中使用的时候也可以直接通过枚举去使用,避免了频繁查询数据库。当然对于开篇存在的问题,各位朋友如果有什么好的建议,还希望朋友们能提供一些较好的思路。再次感谢大家!