EasyExcel 报错 “calling the 'fill' method must use a template”

892 阅读2分钟

一、问题

      在Spring框架下,使用EasyExcel导出文件到HTTP接口,调试报错“calling the 'fill' method must use a template”。百度上给出的答案未能解决问题。

      代码大体如图所示,报错时还没有31行的代码,使用的30行已注释的代码。

二、排查

顺着错误堆栈,很快找到报错的源码位置:

      可见报错 "Calling the 'fill' method must use a template." 是由于ExcelWriter里面的excel模板对象是null导致的。

      对照之前的代码,发现使用EasyExcelService.class.getResourceAsStream(templateFileName); 不会有问题,但是使用 Object.class.getResourceAsStream(templateFileName); 获取到的inputstream就是null。之所以踩到这个坑,是因为复制原来的代码时,看到getResourceAsStream虽然是使用EasyExcelService.class来调用的,但实际是继承自Object.class对象的方法,想当然地感觉可以用Object.class来替换。

      但是实际运作中,对不同的类的对象,调用getResourceAsStream得到了不同的结果(一个拿到了inputStream,另一个是null)。单步debug这个方法,发现了问题的根源:这个方法是依赖对class对象的类加载器,来获取resource的。

      Object.class和业务代码类的class对象,类加载器并不相同。Object.class作为jre的基础类,是由Bootstrap Classloader加载的,而业务的class对象是由Application Classloader加载的。这两个classloader对根目录的定义也不相同,后者的根目录是编译生成的target/classes,业务代码常见的把模板文件放到Spring框架的resources目录下,编译后会放到target/classes文件夹下,就可以被Application Classloader发现并读取,所以使用业务的class对象进行resource获取没有问题。

三、引申

      EasyExcel中的“calling the 'fill' method must use a template”报错,和excel模板的关系比较密切。所以排查此问题时,需围绕excel模板的配置,和代码返回数据对模板的写入等方面做考虑。当然,最有效的方法还是单步debug(虽然也是最累的方法),深入查看组件代码的逻辑,结合搜索组件的工作机制,找到根因。