本文目的
依赖版本冲突如何定位、如何解决
今天有个同事遇到一个诡异的事情,就是明明引用代码有那个方法但运行总报错找不到java.lang.NoSuchMethodError,盲猜版本冲突(遇到很多次都是版本冲突)
问题复现
代码依赖
<dependencies>
<!-- easy-excel https://github.com/alibaba/easyexcel-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.3.3</version>
</dependency>
<!-- word 填充依赖-->
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-base</artifactId>
<version>4.1.2</version>
</dependency>
</dependencies>
测试代码
package com.csh.test.project.word;
import cn.afterturn.easypoi.word.WordExportUtil;
import cn.afterturn.easypoi.word.entity.MyXWPFDocument;
import cn.hutool.core.lang.Assert;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* @author free-bird
* @version 1.0
* @date 2023/3/9 3:05 PM
*/
@Slf4j
public class WordUtils {
/**
* @param params
* @param sourceTemplatePath 源文件模板,resources下全路径
* @return
*/
public static String fillParam(Map<String, Object> params, String sourceTemplatePath) {
Assert.notNull(sourceTemplatePath, "来源文件不能为空");
ClassPathResource classPathResource = new ClassPathResource(sourceTemplatePath);
// 新建临时目录
String tmpDir = "./tmp/";
File file = new File(tmpDir);
if (!file.exists() && file.mkdir()) {
log.info("创建./tmp目录完成");
}
String suffix = sourceTemplatePath.substring(sourceTemplatePath.lastIndexOf("."));
String tmpPath = tmpDir + UUID.randomUUID().toString() + suffix;
try (InputStream inputStream = classPathResource.getInputStream();
FileOutputStream fos = new FileOutputStream(tmpPath)) {
MyXWPFDocument doc = new MyXWPFDocument(inputStream);
WordExportUtil.exportWord07(doc, params);
doc.write(fos);
} catch (Exception e) {
log.error("word fill param exception: ", e);
throw new RuntimeException("word fill param exception");
}
return tmpPath;
}
public static void main(String[] args) {
Map<String, Object> map = new HashMap<>();
map.put("name", "小明");
//doc格式不支持
//org.apache.poi.openxml4j.exceptions.OLE2NotOfficeXmlFileException: The supplied data appears to be in the OLE2 Format. You are calling the part of POI that deals with OOXML (Office Open XML) Documents. You need to call a different part of POI to process this data (eg HSSF instead of XSSF)
//随便在resources下创建一个模板testWordTemplate.docx,内容{{name}}
WordUtils.fillParam(map, "./testWordTemplate.docx");
}
}
运行报错,
Exception in thread "main" java.lang.NoSuchMethodError: org.apache.poi.util.XMLHelper.newDocumentBuilder()Ljavax/xml/parsers/DocumentBuilder;
at org.apache.poi.ooxml.util.DocumentHelper.newDocumentBuilder(DocumentHelper.java:47)
at org.apache.poi.ooxml.util.DocumentHelper.<clinit>(DocumentHelper.java:36
定位问题与解决
通过报错分析问题
XMLHelper.newDocumentBuilder()找不到方法,跟踪到org.apache.poi.ooxml.util.DocumentHelper#newDocumentBuilder,里面 return XMLHelper.newDocumentBuilder();导航到XMLHelper,发现有这个方法的呀!
那大概率是版本冲突了, 但为什么导航过去是有这个方法的,双击shirt全局搜索类XMLHelper,好家伙真的有两个类但不同版本进去发现4.1.2是有这个方法,而4.1.0里没有。
确认问题
应该是加载到4.1.0版本了,但是如何确认的 两个方式: 1.debug 2.使用插件Maven Helper
1、debug
将断点放在org.apache.poi.ooxml.util.DocumentHelper#newDocumentBuilder这,运行debug模式
到了断点打开表达是求值放入 DocumentHelper.class.getClassLoader().findClass("org.apache.poi.util.XMLHelper") 再求值如图所示,再点导航,发现进入的是4.1.0版本
2、Maven Helper
点击pom.xml左下角有Dependency Analyzer再选择Conflicts(展示冲突),左边是冲突内容,右边是来自哪个依赖及其版本层级以及最终使用了哪个依赖(红色方块就是实际使用的依赖版本)
解决
排查poi依赖,因为easypoi-base只是用来填充word模板参数而引入的easyexcel则是主要用来操作excel所以将easypoi-base中依赖版本移除减少出问题的概率(排除依赖需要考虑影响范围,如果都没影响排除谁都无所谓) 排除poi-ooxml-schemas也一样 排除完 点击上面Reimport刷新依赖,等Refresh UI有个感叹号再点击刷新,冲突没有了,再运行则正常结束
最终pom文件
<dependencies>
<!-- easy-excel https://github.com/alibaba/easyexcel-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.3.3</version>
</dependency>
<!-- word 填充依赖-->
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-base</artifactId>
<version>4.1.2</version>
<exclusions>
<exclusion>
<artifactId>poi</artifactId>
<groupId>org.apache.poi</groupId>
</exclusion>
<exclusion>
<artifactId>poi-ooxml-schemas</artifactId>
<groupId>org.apache.poi</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
🤗 总结归纳
java.lang.NoSuchMethodError遇到这个报错基本是版本冲突没得跑,除非是少了依赖 所以需要根据错误分析问题再对症下药,合理使用插件,提高解决问题效率