啥叫类型擦除
看代码与输出
public static void main(String[] args) {
/**
* 类型擦除试验
*/
List<String> sList = new ArrayList<>();
sList.add("a");
sList.add("b");
fillInteger(sList);
System.out.println(sList); // 输出为 [1, 2]
}
private static void fillInteger(List sList) {
sList.clear();
sList.add(1);
sList.add(2);
}
可以看出类型已被擦除,泛型不匹配或不指定只会产生编译告警,并不会产生编译错误,更不会运行错误。
Jeecg的字典翻译功能
1、可以先移步JEECG官网查看@Dict的功能JeecgBoot 文档中心
即如果我的java POJO定义为
/**
* 性别(字典字配置2条记录 [1:男 2:女])
*/
@Dict(dicCode = "sex")
private Integer sex;
前端收到的报文如下,会多显示一个字段
{
"sex": 1,
"sex_dictText": "男"
}
Jeecg字典翻译功能代码
见github,也可自己下源码查看: https://github.com/jeecgboot/jeecg-boot/blob/master/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/DictAspect.java代码主要逻辑
1、只对IPage对象里的recordList有用,且只翻译第一层属性
2、收集所有的@Dict注解集中查库或缓存
3、将record里的T(泛型)对象,替换为JSON对象,并动态给JSON对象添加“sex_dictText”属性,就在这里进行的泛型擦除与重新赋值
为啥要弄清楚这个原理呢,是因为我们想扩展这个功能。我们可能有如下需求
1、非IPage对象也可以使用字典功能
2、对象子对象也可能使用字典功能
字典翻译功能扩展
原理就是参照上面的3个步骤,主要是子对象递归依靠什么规则,我们这里以公司的包名来限定哪些子对象需要递归扩展翻译
代码如下
private Object parseDictText(Object result) {
if (result instanceof Result) {
if (((Result) result).getResult() instanceof IPage) {
......
} else {
// 这里
}
}
找到parseDictText函数,添加else代码块
else 代码块
else {
Object ret = ((Result<?>) result).getResult();
if (ret == null) {
return;
}
ObjectDict objDict = ret.getClass().getAnnotation(ObjectDict.class);
if (objDict == null) {
return;
}
Map<String, Set<String>> dataSetMap = new HashMap<>(5);
try {
// 1. 递归遍历出所有dict,并生成翻译缓存
deepRecallFindDict(ret, dataSetMap);
Map<String, List<String>> dataListMap = new HashMap<>(dataSetMap.size());
dataSetMap.forEach((key, value) -> dataListMap.put(key, new ArrayList<>(value)));
Map<String, List<DictModel>> transResultMap = this.translateAllDict(dataListMap);
// 2. 递归填充所有dict
JSONObject retJson = this.getJsonObject(ret);
deepRecallFillDict(ret, retJson, transResultMap);
((Result) result).setResult(retJson);
} catch (IllegalAccessException e) {
log.warn("", e);
}
}
其它支撑函数
private static final String PACKAGE_SUFFIX = "com.github.你自己的包....";
private JSONObject getJsonObject(Object record) {
ObjectMapper mapper = new ObjectMapper();
String json="{}";
try {
//解决@JsonFormat注解解析不了的问题详见SysAnnouncement类的@JsonFormat
json = mapper.writeValueAsString(record);
} catch (JsonProcessingException e) {
log.error("json解析失败"+e.getMessage(),e);
}
//update-begin--Author:scott -- Date:20211223 ----for:【issues/3303】restcontroller返回json数据后key顺序错乱 -----
JSONObject item = JSONObject.parseObject(json, Feature.OrderedField);
return item;
}
private void deepRecallFillDict(Object ret, JSON retJson, Map<String, List<DictModel>> transResultMap) throws IllegalAccessException {
if (ret == null) {
return;
}
if (ret instanceof Collection) {
Object[] objects = ((Collection<?>) ret).toArray();
int i= 0;
for (Object object : objects) {
deepRecallFillDict(object, (JSON)(((JSONArray)retJson).get(i++)), transResultMap);
}
} else {
if (!ret.getClass().getPackage().getName().startsWith(PACKAGE_SUFFIX)) {
return;
}
JSONObject json = (JSONObject) retJson;
for (Field field : oConvertUtils.getAllFields(ret)) {
field.setAccessible(true);
Object value = field.get(ret);
field.setAccessible(false);
if (value instanceof Collection) {
JSON jsonCollection = (JSON) json.get(field.getName());
deepRecallFillDict(value, jsonCollection, transResultMap);
} else if (field.getType().getCanonicalName().startsWith(PACKAGE_SUFFIX)) {
JSON jsonObj = (JSON) json.get(field.getName());
deepRecallFillDict(value, jsonObj, transResultMap);
}
//update-end--Author:scott -- Date:20190603 ----for:解决继承实体字段无法翻译问题------
if (field.getAnnotation(Dict.class) != null) {
String code = field.getAnnotation(Dict.class).dicCode();
String text = field.getAnnotation(Dict.class).dicText();
String table = field.getAnnotation(Dict.class).dictTable();
String dictCode = code;
if (!org.apache.commons.lang3.StringUtils.isEmpty(table)) {
dictCode = String.format("%s,%s,%s", table, text, code);
}
if (oConvertUtils.isNotEmpty(value)) {
List<DictModel> dictModels = transResultMap.get(dictCode);
if(dictModels==null || dictModels.size()==0){
continue;
}
String textValue = this.translDictText(dictModels, Optional.ofNullable(value).map(String::valueOf).orElse(null));
log.debug(" 字典Val : " + textValue);
log.debug(" __翻译字典字段__ " + field.getName() + CommonConstant.DICT_TEXT_SUFFIX + ": " + textValue);
// TODO-sun 测试输出,待删
log.debug(" ---- dictCode: " + dictCode);
log.debug(" ---- value: " + value);
log.debug(" ----- text: " + textValue);
log.debug(" ---- dictModels: " + JSON.toJSONString(dictModels));
json.put(field.getName() + CommonConstant.DICT_TEXT_SUFFIX, textValue);
}
}
}
}
}
private void deepRecallFindDict(Object ret, Map<String, Set<String>> dataListMap) throws IllegalAccessException {
if (ret == null) {
return;
}
if (ret instanceof Collection) {
Object[] objects = ((Collection<?>) ret).toArray();
for (Object object : objects) {
deepRecallFindDict(object, dataListMap);
}
} else {
if (!ret.getClass().getPackage().getName().startsWith(PACKAGE_SUFFIX)) {
return;
}
for (Field field : oConvertUtils.getAllFields(ret)) {
field.setAccessible(true);
Object value = field.get(ret);
field.setAccessible(false);
if (value instanceof Collection) {
deepRecallFindDict(value, dataListMap);
} else if (field.getType().getCanonicalName().startsWith(PACKAGE_SUFFIX)) {
deepRecallFindDict(value, dataListMap);
}
//update-end--Author:scott -- Date:20190603 ----for:解决继承实体字段无法翻译问题------
if (field.getAnnotation(Dict.class) != null) {
String code = field.getAnnotation(Dict.class).dicCode();
String text = field.getAnnotation(Dict.class).dicText();
String table = field.getAnnotation(Dict.class).dictTable();
Set<String> dataSet;
String dictCode = code;
if (!org.apache.commons.lang3.StringUtils.isEmpty(table)) {
dictCode = String.format("%s,%s,%s", table, text, code);
}
dataSet = dataListMap.computeIfAbsent(dictCode, k -> new HashSet<>());
Optional.ofNullable(value).map(String::valueOf).ifPresent(dataSet::add);
}
}
}
}
用法
使用ObjectDict注解需要返回的类即可
@ObjectDict
public class JeecgOrderCustomer implements Serializable {
String name;
@Dict(dicCode="sex")
int sex;
List<JeecgOrder> orders;
}
public class JeecgOrder implements Serializable {
String id;
@Dict(dicCode="category")
int category;
}
那在controller里调用时,只要使用Result包装,即可生效
public Result<?> queryOrderCustomerListByMainId(@RequestParam(name = "id", required = true) String id) {
List<JeecgOrderCustomer> jeecgOrderCustomerList = jeecgOrderCustomerService.selectCustomersByMainId(id);
return Result.ok(jeecgOrderCustomerList);
}
扩展功能的不足与注意
1、扩展功能的范围是以包命来限制的,所以这里需要配置包名,可能也是因为这个原因官方才没有做这个扩展
2、扩展暂时不支持Map
3、代码中ObjectDict是一个注解,仅在最外层的Class上注解可以生效