前置知识:
需要会自定义注解方法自定义注解字段AOP切面编程,反射等...
核心代码结构:
核心代码实现:
package com.***.config;
import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Maps;
import com.***.common.entity.dto.PageResult;
import com.***.common.utils.R;
import com.***.util.DictUtil;
import lombok.SneakyThrows;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@Component
@Aspect
public class IntdDictMethodAop {
@SneakyThrows
@Around("@annotation(IntdDictMethod)")
public Object around(ProceedingJoinPoint pjp) {
Object proceed = pjp.proceed();
Map<String, Map<String, String>> map = Maps.newHashMap();
if (proceed instanceof R) {
Object data = objParse(R.class, proceed).getData();
if (data != null) {
if (data.getClass().isArray() || data instanceof Collection) {
Collection<Object> collections = listParse(Object.class, data);
//如果返回数据是数组或集合,则遍历其中的每个元素并调用 convertData 方法进行转换
for (Object datum : collections) {
convertData(datum, map);
}
} else if (data instanceof PageResult) {
Collection<Object> innerData = listParse(Object.class, objParse(PageResult.class, data).getData());
for (Object obj : innerData) {
convertData(obj, map);
}
} else {
convertData(data, map);
}
}
} else if (proceed instanceof PageResult) {
Collection<Object> innerData = listParse(Object.class, objParse(PageResult.class, proceed).getData());
for (Object obj : innerData) {
convertData(obj, map);
}
} else if (proceed instanceof Collection) {
Collection<Object> collections = listParse(Object.class, proceed);
for (Object datum : collections) {
convertData(datum, map);
}
} else {
convertData(proceed, map);
}
return proceed;
}
public static <T> T objParse(Class<T> clazz, Object value) {
return clazz.cast(value);
}
public static <T> Collection<T> listParse(Class<T> clazz, Object obj) {
if (obj == null) {
return new ArrayList<>();
}
Collection<T> result = new ArrayList<>();
for (Object o : (Collection<?>) obj) {
result.add(clazz.cast(o));
}
return result;
}
@SneakyThrows
private void convertData(Object o, Map<String, Map<String, String>> map) {
if (o == null) {
return;
}
Class<?> sourceClass = o.getClass();
for (; sourceClass != Object.class; sourceClass = sourceClass.getSuperclass()) {
Field[] fields = sourceClass.getDeclaredFields();
for (Field field : fields) {
Class<?> type = field.getType();
field.setAccessible(true);
if (type.getClassLoader()!=null) {
Object o1 = field.get(o);
convertData(o1,map);
continue;
}
if (List.class.isAssignableFrom(type)) {
List fieldListObj = (List) field.get(o);
if (fieldListObj != null && fieldListObj.size() > 0) {
for (int i = 0; i < fieldListObj.size(); i++) {
Object o1 = fieldListObj.get(i);
if (o1 != null) {
if (o1.getClass().getClassLoader() != null) {
convertData(o1,map);
}
}
}
}
continue;
}
IntdDict intdDict = field.getAnnotation(IntdDict.class);
if (intdDict == null) {
continue;
}
String typeCode = getTypeCode(intdDict, sourceClass, o);
if (StrUtil.isEmpty(typeCode)) {
continue;
}
String dictCodeField = crmDict.dictCodeField();
Field declaredField = sourceClass.getDeclaredField(dictCodeField);
declaredField.setAccessible(true);
String dictCode = null;
if (declaredField.getType().equals(String.class)) {
dictCode = (String) declaredField.get(o);
}
if(declaredField.getType().equals(Integer.class)){
dictCode = declaredField.get(o)!=null ? declaredField.get(o).toString() : null;
}
if (StrUtil.isEmpty(dictCode)) {
continue;
}
Map<String, String> dictTypeMap = map.get(typeCode);
if (dictTypeMap != null) {
String val = dictTypeMap.get(dictCode);
if (!StrUtil.isEmpty(val)) {
field.set(o, val);
}
} else {
DictUtil dictUtil=new DictUtil();
Map<String, String> dictMap = dictUtil.dictMap(typeCode);
if (dictMap != null) {
map.put(typeCode, dictMap);
String val = dictMap.get(dictCode);
if (!StrUtil.isEmpty(val)) {
field.set(o, val);
}
}
}
}
}
}
@SneakyThrows
private String getTypeCode(IntdDict intdDict, Class<?> sourceClass, Object o) {
String type = intdDict.type();
if ("1".equals(type)) {
return intdDict.typeCode();
} else if ("2".equals(type)) {
String typeCode = intdDict.typeCode();
Field typeCodeField = sourceClass.getDeclaredField(typeCode);
typeCodeField.setAccessible(true);
return (String) typeCodeField.get(o);
}
return null;
}
}
核心代码是一个数据转换操作的方法,主要作用是将 Java 对象中被自定义注解@IntdDict指定的字段上的字典编码转换为字典值。下面我逐行解析这个方法是如何实现的。
-
第一行的方法签名
private void convertData(Object o, Map<String, Map<String, String>> map)
中,参数o
是需要进行字典编码转换的 Java 对象,参数map
是存储字典数据的双重映射表。 -
第二行判断对象是否为空,如果为空则直接返回。
-
第三行获取对象的类类型,并遍历该类及其父类上的所有成员变量。
-
第四行获取当前成员变量的类型,并设置该成员变量可访问。
-
第五行判断当前成员变量的类型是否为自定义类型,如List等。如果是自定义类型,则递归调用
convertData
方法,继续进行字典编码转换。 -
第九行判断当前成员变量上是否存在
IntdDict
注解,如果不存在则继续遍历下一个成员变量。 -
第十行调用
getTypeCode
方法获取该成员变量的字典类型编码。 -
第十二到第二十七行,根据成员变量的字典类型编码和字典编码,从
map
中获取相应的字典值并设置回成员变量上。如果map
中没有包含该字典类型,则调用DictUtil
类获取该字典类型的字典数据,并将其存储到map
中。 -
最后一行结束方法的执行。
总体来说,这段代码实现了一个自动化的 Java 对象转换工具,能够快速地将对象中的字典编码转换为相应的字典值,大大简化了对字典数据的操作。它的主要思路是通过反射获取对象属性上的注解信息,然后通过该注解信息获取需要进行转换的字典编码和字典类型,最后从预先加载到内存中的 map
表中获取相应的字典值并设置回属性中。该代码通过提高字典数据的查询效率和方便性,大大提高了开发效率并优化了程序性能。
优点:这个工具类被我封装为了一个starter,有微服务模块都能引用我这个代码,实现低耦合,开发过程只需要引用我这个自定义注解即可实现功能。且前端开发也无需自己处理这类数据,直接获取展示到界面就可以。
缺点:这个递归写的不是很完美,途中出过bug,调试了很久发现不能在实体类上用这个注解@Slf4j不然递归会很深导致栈溢出,需要继续完善,测试。