JVM的工作原理
JVM的設(shè)計(jì)理念極其簡(jiǎn)單口叙,JVM只會(huì)做兩件事情:
- 執(zhí)行一個(gè)類的字節(jié)碼
- 執(zhí)行這個(gè)類的時(shí)候,若遇到了新的類嗅战,那么就加載它
- 不斷重復(fù)以上兩個(gè)過(guò)程妄田,直到結(jié)束
JVM通過(guò)classpath參數(shù)來(lái)獲取加載類的路徑,這些包以冒號(hào)分割驮捍,當(dāng)JVM遇到了依賴的類時(shí)疟呐,就會(huì)去classpath路徑中依次尋找直到找到這個(gè)類為止。
什么是包管理
在沒(méi)有包管理這樣一個(gè)概念時(shí)厌漂,程序員需要手動(dòng)編譯這些類,類之間的相互依賴使得這個(gè)過(guò)程非常的繁瑣斟珊,而且苇倡,還會(huì)出現(xiàn)依賴沖突富纸。于是乎,就出現(xiàn)了包管理的概念
包管理的本質(zhì)就是告訴JVM如何找到所需要的第三方類庫(kù)旨椒,并且陳工地解決依賴沖突的問(wèn)題晓褪。
依賴沖突
在Maven誕生前,依賴沖突是一個(gè)非常容易發(fā)生且難以解決的問(wèn)題综慎。全限定類名是JVM眼中識(shí)別類的唯一標(biāo)識(shí)涣仿,如果多個(gè)同名類的不同版本出現(xiàn)在classpath中,就可能會(huì)帶來(lái)沖突的問(wèn)題示惊,例如:
你的項(xiàng)目依賴了B包和C包的兩個(gè)版本好港,包的依賴路徑被完整地寫(xiě)到了classpath上,JVM會(huì)從前向后在classpath上尋找需要加載的類米罚,所以如果C1包在C2包的前面钧汹,那么JVM就會(huì)忽視C2包,C2包的類是C包的后期版本录择,可能新添加了某些功能拔莱,修復(fù)了一些bug,這樣一來(lái)隘竭,項(xiàng)目就有可能因?yàn)橐蕾嚊_突而出錯(cuò)塘秦。
像以下幾種異常:
- AbstractMethodError
- NoClassDefFoundError
- ClassNotFoundException
- LinkageError
如果項(xiàng)目中報(bào)了以上的異常,很有可能一個(gè)原因是因?yàn)橐蕾嚊_突引起的动看。
Maven 解決沖突的原則
在了解Maven是如何解決沖突之前尊剔,我們先來(lái)了解一下Maven是如何對(duì)包進(jìn)行管理的
Maven規(guī)定了生產(chǎn)代碼在src/main目錄。測(cè)試代碼在src/test目錄弧圆,這個(gè)是不成文的規(guī)定赋兵,這也是Maven的約定優(yōu)于配置原則(Convention over configuration),在沒(méi)有強(qiáng)制的規(guī)定時(shí)搔预,因?yàn)槊總€(gè)人定義的目錄不同霹期,這就造成了很多問(wèn)題,Maven則強(qiáng)制規(guī)定了這樣的目錄結(jié)構(gòu)拯田。
Maven有中央倉(cāng)庫(kù)以及本地倉(cāng)庫(kù)历造。
本地倉(cāng)庫(kù)默認(rèn)在~/.m2
下,這個(gè)目錄存放著下載的第三方包緩存船庇。
中央倉(cāng)庫(kù)則是線上倉(cāng)庫(kù)吭产,通過(guò)groupId,artifactId,version這樣的坐標(biāo)信息定位到我們需要的第三方包
我們?cè)趐om.xml文件中配置好坐標(biāo)信息,Maven就會(huì)自動(dòng)下載這些包以及相關(guān)的依賴包鸭轮,并在本地倉(cāng)庫(kù)中緩存起來(lái)臣淤。
那么 Maven是如何解決包的沖突的呢?
拿之前的例子說(shuō)明:
Maven會(huì)保留離項(xiàng)目最近的包窃爷,刪去其他的包邑蒋,本示例中姓蜂,離項(xiàng)目最近的包是C2。Maven會(huì)去除掉C1而保留C2,當(dāng)然這種策略也是不完美的医吊,不過(guò)相對(duì)于自己操作實(shí)在是方便多了钱慢。
如何解決Maven的依賴沖突
有的時(shí)候,即便是Maven包管理工具卿堂,還是可能造成項(xiàng)目依賴沖突束莫,解決Maven的沖突有如下幾種辦法:
-
直接依賴最高版本
在沒(méi)有添加紅色的依賴時(shí),Maven會(huì)比較C1和C2兩個(gè)包離項(xiàng)目的遠(yuǎn)近草描,之后會(huì)比較先后的次序览绿,所以Maven會(huì)舍棄掉C2包,我們解決這個(gè)沖突最直接的辦法就是陶珠,直接讓該項(xiàng)目依賴C3最高版本的包挟裂,這樣就可以解決掉沖突的問(wèn)題。
- 使用exclusions排除保重的后代指定的依賴
<dependency>
<groupId>xxx</groupId>
<artifactId>xxx</artifactId>
<version>1.0.0</version>
<exclusions>
<exclusion>
<groupId>yyy</groupId>
<artifactId>yyy</artifactId>
</exclusion>
</exclusions>
</dependency>
如果使用IDEA可以下載mvn helper 插件更加直觀有效解決包沖突的問(wèn)題揍诽。
參考文章 Java包管理以及Maven包管理