咱們程序員在開發的時候常常會遇到各類各樣的 BUG 問題,其中大部分是業務邏輯異常,還有一些是代碼書寫不規範形成的異常例如:NullPointException(NPE),IndexOutOfBoundsException 等等,其實這些咱們都好定位和修復。可是還有一些運行時異常定位起來是特別頭疼的,那就是 jar 包衝突引發的異常。html
通常程序在運行時發生相似於 java.lang.ClassNotFoundException,Method not found: '......',或者莫名其妙的異常信息,這種狀況通常很大可能就是 jar包依賴衝突的問題引發的了。java
至於爲何會發生 jar包依賴衝突?這種問題大體能夠概括爲以下幾個緣由:程序員
能夠看到,其實總的來講 jar 包衝突的主要緣由就是依賴的版本衝突。web
項目中須要導出報表,技術選型的時候,通常是選用 Apache POI,可是 POI 的使用方式比較基礎,開發量大,容易出現內存溢出的問題。apache
考慮到阿里開源了一套解析和生成Excel的工具 - EasyExcel,具備避免內存溢出OOM的狀況發生,並且使用方便簡單,因此就將它引入到了咱們的項目中,具體的使用版本是 1.0.2。dom
<dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>1.0.2</version> </dependency>
而另外一個模塊須要使用 POI 的將 Word 轉成 PDF 的功能,因此同時又引入了以下 POI 的依賴:xss
<!-- poi utils --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.15</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>3.15</version> </dependency>
咱們從 Maven Repository 能夠發現,阿里 EasyExcel 1.0.2 依賴的 POI 也是 3.15,因此照理說應該是沒問題的。maven
可是在接口調試的時候仍是出問題了,並且異常信息很奇怪,不是看一眼就能知道問題緣由的並解決的。工具
Caused by: java.lang.AbstractMethodError: org.apache.xerces.dom.DocumentImpl.getXmlStandalone()Z at com.sun.org.apache.xalan.internal.xsltc.trax.DOM2TO.setDocumentInfo(DOM2TO.java:377) at com.sun.org.apache.xalan.internal.xsltc.trax.DOM2TO.parse(DOM2TO.java:131) at com.sun.org.apache.xalan.internal.xsltc.trax.DOM2TO.parse(DOM2TO.java:98) at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transformIdentity(TransformerImpl.java:693) at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:737) at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:351) at org.apache.poi.openxml4j.opc.StreamHelper.saveXmlInStream(StreamHelper.java:80) at org.apache.poi.openxml4j.opc.internal.marshallers.ZipPartMarshaller.marshallRelationshipPart(ZipPartMarshaller.java:181) at org.apache.poi.openxml4j.opc.ZipPackage.saveImpl(ZipPackage.java:560) at org.apache.poi.openxml4j.opc.OPCPackage.save(OPCPackage.java:1557) at org.apache.poi.POIXMLDocument.write(POIXMLDocument.java:248) at org.apache.poi.xssf.streaming.SXSSFWorkbook.write(SXSSFWorkbook.java:941) at com.alibaba.excel.write.ExcelBuilderImpl.finish(ExcelBuilderImpl.java:64) at com.alibaba.excel.ExcelWriter.finish(ExcelWriter.java:95) at com.pingan.haofang.creams.common.utils.ExcelUtil.writeExcel(ExcelUtil.java:71) ...... ... 65 common frames omitted
提取關鍵信息,能夠看到錯誤類型 java.lang.AbstractMethodError
,這個錯誤類型望名知義:抽象方法錯誤。這種類型的錯誤和咱們上面說的 ClassNotFoundException 相似,很大可能就是 Jar包依賴衝突所致使的。測試
那咱們來定位下是哪一個 jar 包衝突了,只須要將衝突的 jar 包排除掉,留下正確的就能夠了。
咱們能夠看到錯誤類型是 java.lang.AbstractMethodError,錯誤類型後面是具體的錯誤信息描述 :org.apache.xerces.dom.DocumentImpl.getXmlStandalone()Z,意思是在包 org.apache.xerces.dom 下的類DocumentImpl
它的方法getXmlStandalone()
調用出現了錯誤。
那麼具體是誰在調用呢?咱們在異常信息的緊密下一行能夠看到以下這一行代碼:
at com.sun.org.apache.xalan.internal.xsltc.trax.DOM2TO.setDocumentInfo(DOM2TO.java:377)
在包路徑 com.sun.org.apache.xalan.internal.xsltc.trax 下,DOM2TO
類代碼的的第377行,有個setDocumentInfo
方法,咱們鼠標左鍵點進去,在該行加個 Debug 斷點。
咱們發現這個 DOM2TO 類是 JDK1.8中 rt.jar 包裏面的,具體類路徑以下:
經過斷點調試得知,這個 document 對象是 DocumentImpl 實例,
這個DocumentImpl 的真實路徑也是 JDK1.8中 rt.jar 包裏面的,它是 CoreDocumentImpl 的子類,CoreDocumentImpl 是接口Document 的實現類。
package com.sun.org.apache.xerces.internal.dom; public class DocumentImpl extends CoreDocumentImpl implements DocumentTraversal, DocumentEvent, DocumentRange { ...... }
CoreDocumentImpl
package com.sun.org.apache.xerces.internal.dom; public class CoreDocumentImpl extends ParentNode implements Document { ...... }
咱們在 CoreDocumentImpl 類中第983行發現了getXmlStandalone
方法。
這時報錯緣由赤條條的擺在咱們面前了,顯而易見,DOM2TO類中 setDocumentInfo 方法的參數 Document 是屬於 JDK1.8 中 rt.jar 包下類路徑 com.sun.org.apache.xerces.internal.dom 下的實現類 DocumentImpl。而咱們報錯的信息提示中是:
Caused by: java.lang.AbstractMethodError: org.apache.xerces.dom.DocumentImpl.getXmlStandalone()Z
這個 org.apache.xerces.dom.DocumentImpl 明顯不屬於咱們 JDK1.8 的 rt.jar 包,並且也沒有 getXmlStandalone 這個方法。
因此得知,個人項目中 jar 包依賴衝突了,咱們只須要排除掉 org.apache.xerces.dom.DocumentImpl 所屬的 jar 包就能夠了。如何排除呢?
咱們在 IDEA 中雙擊 Shift 鍵,輸入 DocumentImpl,獲得以下結果:
能夠發現,這裏有兩個 CoreDocumentImpl,一個是咱們的 JDK1.8的,一個是屬於 xerce的,並且確實在依賴的 maven jar 包中發現了 xercesImpl-2.4.0.jar,這個 jar包就是須要排除的 jar包。
發現了衝突的 jar包,我全局搜索關鍵字 xerces,並無發現哪個 pom 中有依賴的代碼,因此極可能是其餘的 jar 包傳遞依賴進來的。
咱們藉助 IDEA 的 maven 工具,在 maven 欄右鍵項目模塊,選擇 show Dependencies
或 Ctrl + Shift + Alt + U
,這時候會展現當前模塊的 jar 包依賴圖,以下:
雖然這裏展現了不少衝突的jar包,其中紅線鏈接的就是衝突的jar 包,可是咱們 Ctrl + F
查詢 xerces 仍是沒有結果。
因此咱們須要額外的方式來解決,這時我想到了 IDEA 有個插件 Maven Helper
,具體的插件下載能夠參考前面的內容,下載好插件後,咱們打開 pom.xml 文件,在pom.xml 文件的左下方有個 Dependency Analyzer
,咱們點擊以後顯示以下:
咱們輸入 xerces,選擇以樹形展現全部依賴,獲得以下的信息顯示。
清晰明瞭,原來這個罪魁禍首是被 file-web-sdk 帶進來的,咱們右鍵選擇 Jump To Source
或者 F4 定位到這個 jar 在 pom.xml 的依賴引入位置,以下圖所示,咱們經過 exclusion 標籤排除 xercesImpl 的引入便可。
<dependency> <groupId>com.xx.xx.gov.fileservice</groupId> <artifactId>file-web-sdk</artifactId> <exclusions> <exclusion> <groupId>xerces</groupId> <artifactId>xercesImpl</artifactId> </exclusion> </exclusions> </dependency>
再次啓動項目,測試接口發現功能正常了,整個排查過程也就結束了,IDEA的功能仍是很強大的。
不少時候的 jar 包衝突,有些是咱們很容易排除,例如在pom.xml 中咱們就能夠發現一些重複引入,可是版本不相同的依賴。還有一些是其餘依賴傳遞依賴進來的,咱們在 pom.xml 文件中不能很直觀的發現,這時候咱們藉助工具能夠發現這種衝突的依賴。
可是還有一些是更隱祕的衝突,就像本文中描述的依賴衝突,這時候咱們須要分析異常信息,並定位衝突的緣由和找到具體衝突的依賴引入,最後將它排除就能夠了。
本文比較詳細的介紹了異常的分析和衝突的定位,以及最後的排除。相似的依賴衝突基本均可以參考上述的方式進行排查,但願經過本篇文章對你們解決項目中依賴衝突有所幫助。