从jeecg开源代码看泛型类型擦除之妙用

啥叫类型擦除

看代码与输出

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上注解可以生效

  • 10
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值