今天在开发的时候遇到一个问题,场景是这样的,查询支出类别,我将某个公司的部门结构构建成一颗多级的分类树,如果当前的支出类别,配置了只有某些固定的部门才能查看,我就需要用这个配置的部门id去查找这颗树,查询到一颗字树,然后通过token去拿到当前登录用户的地区,如果当前登录的用户的部门,包含在这颗字树,则当前这个用户能看到这个支出类别的选项.那么问题就来了.
这里我一共遇到两个坑,我先说第一个,也就是标题所示的hutool的TreeUtil的getNode方法的空指针异常.
项目启动后发现报错,我在main放大里面自己通过TreeUtil构建一颗树,代码如下
public static void main(String[] args) {
List<TreeNode<Integer>> list = new ArrayList<>();
TreeNode<Integer> treeNode1 = new TreeNode<>();
treeNode1.setId(1);
treeNode1.setParentId(0);
list.add(treeNode1);
TreeNode<Integer> treeNode2 = new TreeNode<>();
treeNode2.setId(2);
treeNode2.setParentId(1);
list.add(treeNode2);
TreeNode<Integer> treeNode3 = new TreeNode<>();
treeNode3.setId(3);
treeNode3.setParentId(1);
list.add(treeNode3);
TreeNode<Integer> treeNode4 = new TreeNode<>();
treeNode4.setId(21);
treeNode4.setParentId(2);
list.add(treeNode4);
TreeNode<Integer> treeNode5 = new TreeNode<>();
treeNode5.setId(31);
treeNode5.setParentId(3);
list.add(treeNode5);
TreeNode<Integer> treeNode6 = new TreeNode<>();
treeNode6.setId(211);
treeNode6.setParentId(21);
list.add(treeNode6);
List<Tree<Integer>> build = TreeUtil.build(list);
TreeUtil.getNode(build.get(0),31);
}
如代码所示,当去读取节点1,2,21这三个节点都能读取,但是这棵树存在节点31,我去读取的时候,却出现了空指针就很奇怪了,不只是31,从顶节点的第二字节点开始就都读取不到了,堆栈信息如下:
Exception in thread "main" java.lang.NullPointerException
at cn.hutool.core.lang.tree.TreeUtil.getNode(TreeUtil.java:139)
at cn.hutool.core.lang.tree.Tree.getNode(Tree.java:56)
at cn.hutool.core.lang.tree.TreeUtil.getNode(TreeUtil.java:140)
at cn.hutool.core.lang.tree.Tree.getNode(Tree.java:56)
at cn.hutool.core.lang.tree.TreeUtil.getNode(TreeUtil.java:140)
at cn.hutool.core.lang.tree.Tree.getNode(Tree.java:56)
at cn.hutool.core.lang.tree.TreeUtil.getNode(TreeUtil.java:140)
at com.jiuji.oa.finance.sysconfig.service.impl.InvoiceDocConfigServiceImpl.main(InvoiceDocConfigServiceImpl.java:1120)
于是我去阅读了源码,发现问题所在
![](https://img-blog.csdnimg.cn/img_convert/4b9710bb9359a2553ad9e4d49c89b499.png)
如上图所示,查询过程中,递归去遍历,但是去问题就出在这里,递归的默认条件是当前节点必定含有子节点,所以当我们遍历到最底层的节点时,他并没有子节点,所以for循环遍历node.getChildren就会出现空指针异常,那么找到问题了就好解决了,我们重写他的方法.
![](https://img-blog.csdnimg.cn/img_convert/edac7b290857400786bf0e08f679ca38.png)
使用我自己定义后的方法去重新读取,结果和预想的一样,成功了,解决了第一个问题.
![](https://img-blog.csdnimg.cn/img_convert/8cb0d781a50e7387916aeb41c7a1bd85.png)
第二个问题就是,多层嵌套结构,反序列化的问题,因为制作这样的一颗树,我需要将整个depart表,全拉一份,但是这个接口却是经常使用,每次都全量拉表肯定受不了,所以我将这颗树,存入redis中,生命周期为24小时,这样一天只需拉取一次全表即可.问题也由此而来,先看我取出缓存的代码
![](https://img-blog.csdnimg.cn/img_convert/08dfeaf91849f70423dab765f7d38c01.png)
当有缓存的时候,我会反序列化这棵树,但是反序列化的结果却是有问题的,以下两张图可以看出差别
![](https://img-blog.csdnimg.cn/img_convert/57db48e5aeeb8607d73d4dc4b5ef042b.png)
可以看到,正常的Tree结构,他的Children是一个ArrayList,并且每个节点的结构都是Tree,再看反序列化的结果.
![](https://img-blog.csdnimg.cn/img_convert/70b6141da50cb0676518a5952a0d5dbf.png)
最外层确实反序列化为Tree了,但是内层的结构却还是JSONArray和JSONObject,以至于我后续去用这棵树的getNode方法的时候会出现类型强制转换异常,原因就是JSONObject不能转换成Tree.我去查阅了一定的资料,发现可以使用 new TypeReference去解决
JSON.parseObject(simpleDiffot, new TypeReference<Tree<Integer>>() {});
但是很遗憾,我不知道是我使用的方式不对还是什么,这个问题并没有得到解决,我想到的原因可能是因为 new TypeReference只能解决两层的嵌套,但是如果是像Tree这种,可能存在五六级的嵌套,还是无法解决.这里也是卡了我很久,没有一个好的解决方式,如果哪位大哥有好的解决方式,请给我分享.
接下来,和大家说一下我的笨办法,没错,每当调用getNode的时候,在递归的过程中手动将其反序化.
![](https://img-blog.csdnimg.cn/img_convert/20cd804e79d476db9e1561faa6861920.png)
也就是红框下面的代码.因为代码使用了其他的JSONArray所以,这里要写全包名,后面看不到的部分是Tree.class.问题到此解决完了,如有错误,请不吝赐教!