History
- Xerces是Java生態圈使用最廣泛的XML解析器,基本上所有的類庫和框架都會在一定程度上使用它(即使沒有直接引用,也有可能間接引用)
- Xerces在官網中發布的包是沒有標注版本的,2.11.0的jar命名為xercesImpl.jar而不是xercesImpl-2.11.0.jar.
- Xerces不使用Maven,不會上傳官方的release版本到Maven的中央倉庫。
- Xerces以前是發布一個jar包,但是之后分成兩個jar包發布,一個包含API的xml-apis.jar和另一個包含其實現的xercesImpl.jar。許多老點兒的Maven Pom仍然依賴xerces.jar。更早的時候Xerces發布一個xmlParserAPIs.jar,也有些很古老很古老的pom會依賴這個jar。
- 發布到Maven倉庫的一些jar經常會依賴版本不同的xml-apis和xercesImpl。舉例來說,依賴的xml-apis的版本可能是1.3.03而依賴的xercesImpl版本可能就是2.8.0,即使兩個包都是來自
Xerces 2.8.0。因為人們使用xml-apis.jar只想使用它某個版本的特定的API。 - 更麻煩的是,JRE中JAXP(Java API for XML Processing)的Reference implementation(參考實現?)使用的XML解析器就是用的這個鳥Xerces。實現類被重新打包進了com.sun.,從而導致直接引用這些類很危險,因為他們在某些JRE中可能不會包含。然而,Xerces中並不是所有的方法都通過java.和javax.*的API暴露。舉例來說,就沒有實現Xerces序列化的API。
- 基本上所有的servlet容器(JBoss, Jetty, Glassfish, Tomcat, ====),都包含一份或多份Xerces在他們的/lib包下。
問題
Maven解決jar包沖突
正是由於以上的一個或幾個問題,許多組織會在他們的pom中重新構建自己的Xerces。當你的工程比較小而且你在用maven中央倉庫的時候,這沒什么問題。但是當你用Artifactory或者Nexus代理你的多個倉庫時(JBoss的,Hibernate的,==),可能就出現問題了。
舉例來說,A組織可能以如下方式依賴xml-apis:
<groupId>org.apache.xerces</groupId>
<artifactId>xml-apis</artifactId>
<version>2.9.1</version>
而B組織可能以如下方式依賴同樣的jar包:
<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
<version>1.3.04</version>
盡管B的jar版本比A的jar版本低,但是maven並不知道他們倆是同一個artifact,因為兩個jar的groupId不同,那么,最后maven解決版本的沖突而是把兩個jar同時引入項目。如下圖:
類加載器Hell
上面提到的,JRE會在JAXP RI中包含Xerces。當你把maven依賴的所有的Xerces都標記成<exclusion>或者<provided>雖然看起來沒什么問題,但是你依賴的第三方代碼可能和你使用的JDK中的JAXP中Xerces版本不兼容。除此之外,你還要應付servlet容器中的包含的Xerces。這給了你幾個選擇:
- 刪除servlet中的Xerces,然后祈禱你的容器可以在JAXP提供的版本上運行。
- 保留servlet中的那個版本,然后祈禱你的應用框架可以在servlet提供的Xerces版本上正常運行。
- 如果最后你的產品有那么一個或者兩個沒解決的版本沖突(如果你的應用很大的話,這是很容易出現的情況),你很快就會發現你正處於類加載器地獄(ClassLoader Hell),疑惑類加載器到底他媽的挑了那個版本在運行?在window或者linux上會不會挑同一個版本(很有可能不會)。
解決方案?
我們嘗試把所有的Xerces的maven依賴標記成 或者,但是由於這鳥貨的別名太多了(xml-apis, xerces, xercesImpl, xmlParserAPIs,====),這有時候並無卵用。另外,我們依賴的第三方包或者框架很可能不會跑在JAXP的版本或者servlet容器提供的Xerces版本上。
到底怎么解決?
目前他們的解決方案如下:
可以嘗試使用maven enforcer插件中的banned dependency。這個rule可以讓你禁止所有你不喜歡的別名,而且只會加入你喜歡的。