1. 程式人生 > >永遠不要在Classpath裡邊放有版本衝突的jar包

永遠不要在Classpath裡邊放有版本衝突的jar包

很多人都說這個問題太明顯,這你還要說。雖然很明顯,但是人們還是不斷的犯錯誤。尤其是有時候僥倖心裡給了人們可稱之機。

只說一個web應用的例子吧。我們一般會在WEB-INF/lib目錄中放自己需要的jar包。遠古時代,當程式設計師自己手工管理這些jar包的時候,也不太會什麼問題。可是有了Maven之後,程式設計師只是在pom.xml中申明自己需要的jar包,Maven會代勞地將這些jar包最終打在WEB-INF/lib中。再加上Maven的依賴傳遞、版本仲裁等牛X的機制,可以說很多程式設計師最後自己也不知道WEB-INF/lib下面到底打了哪些jar包的哪個版本。

如果碰巧,通過亂七八糟的依賴,你的classpath中有多個不同版本的spring的jar包。有人說:“什麼?那不可能。”別急,我指得是這種情況。

WEB-INF/lib/spring-2.0.1.jar
WEB-INF/lib/spring-beans-2.5.4.jar
WEB-INF/lib/spring-core-2.5.4.jar

由於Maven的版本仲裁機制,我們是不可能同時有兩個spring-*.*.*.jar。但是我們可以有一個spring完整的jar和兩個spring modules jar包,而且他們不是同一版本的。如果版本仲裁控制的不好,這非常有可能。

然後……天下大亂了……

也許你會說,只要JVM保證每次載入的,要麼一直是老版本的jar包或者地一直是新版本的jar,那就沒事兒了。因為,另一個版本的jar包因了排在了classpath後邊,是永遠載入不到的。如果有這樣的想法,那你就錯了。沒幾天你就會崩潰的發現,JVM是一會載入老版本,一會載入新版本,有時候還混合著載入,再丟擲一大堆ClassNotFoundException跟你玩。為什麼會這樣?

拿JBoss舉例吧,JBoss說:“我是用java.io.File.listFiles()來列出來WEB-INF/lib目錄下的jar包,然後嚴格地按順序從前往後,一個jar包一個jar包的查詢類的。”

嗯,看起來JBoss還挺嚴禁的。再看看Java怎麼說,java.io.File.listFiles()的Javadoc說:“……There is no guarantee that the name strings in the resulting array will appear in any specific order; they are not, in particular, guaranteed to appear in alphabetical order.……”

啊?什麼意思?它說它不保證返回結果的順序?尤其是不保證是按檔名字典序排。也就是說好一點的情況,你在同一臺機器,同一個版本的JVM上執行,沒準兒每次返回的檔案列表順序還能相同。但是一旦部署到線上的機器,或者換了另一個開發環境,完全有可能listFiles()返回的檔案列表順序是不一樣的。所以我們完全不能做任何關於順序的假設。

所以,也就是說如果你的WEB-INF/lib/下面放了多個不同版本的jar包,你是完全無法控制JVM或者說JBoss去先載入哪個jar包中的類的。還是老老實實的想辦法,把版本通過Maven仲裁掉。如果兩部分程式碼確實要依賴兩個不同版本的Spring,那恭喜你,發程式碼吧。