自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

程序員需要了解依賴沖突的原因以及解決辦法

開(kāi)發(fā)
依賴沖突是日常開(kāi)發(fā)中經(jīng)常碰到的過(guò)程,如果運(yùn)氣好,并不會(huì)有什么問(wèn)題。偏偏阿粉有點(diǎn)背,碰到好幾次生產(chǎn)問(wèn)題,排查一整晚,最后發(fā)現(xiàn)卻是依賴沖突的引起的問(wèn)題。

前言

依賴沖突是日常開(kāi)發(fā)中經(jīng)常碰到的過(guò)程,如果運(yùn)氣好,并不會(huì)有什么問(wèn)題。偏偏阿粉有點(diǎn)背,碰到好幾次生產(chǎn)問(wèn)題,排查一整晚,最后發(fā)現(xiàn)卻是依賴沖突的引起的問(wèn)題。

沒(méi)碰到過(guò)這個(gè)問(wèn)題同學(xué)可能沒(méi)什么感覺(jué),阿粉舉兩個(gè)最近碰到例子,讓大家感受一些。

例子 1:

我們公司有個(gè)古老的業(yè)務(wù)基礎(chǔ)包 A。B,C 業(yè)務(wù)依賴這個(gè)包。某個(gè)團(tuán)隊(duì)拷貝 A 的部分代碼進(jìn)行重構(gòu),類(lèi)名與路徑完全一樣,然后重新打包成 D 發(fā)布。

一次業(yè)務(wù)改動(dòng),B 業(yè)務(wù)也引入了 D 包,測(cè)試環(huán)境運(yùn)行的時(shí)候,一切 OK,但是在生產(chǎn)運(yùn)行時(shí),卻拋出 NoSuchMethodError。

問(wèn)題原因在于 B 業(yè)務(wù)依賴 A,D。而 A,D 存在兩個(gè)同包同名類(lèi),運(yùn)行的時(shí)候,具體加載誰(shuí),不同環(huán)境還真不一樣。

例子 2:

A 業(yè)務(wù)使用 Dubbo 進(jìn)行 RPC調(diào)用, Dubbo 需要依賴 javassist。當(dāng)前依賴關(guān)系為: 

  1. A------->Dubbo------->javassist-3.18.1.GA 

某次改動(dòng)中引入另外一個(gè)第三方開(kāi)源包,其依賴 javassist-3.15.0-GA 。生產(chǎn)發(fā)布的時(shí)候,將 javassist-3.15.0-GA 打包到應(yīng)用中,由于生產(chǎn)環(huán)節(jié)為 JDK1.8,從而導(dǎo)致運(yùn)行直接失敗。

除了上述問(wèn)題,依賴沖突還可能導(dǎo)致應(yīng)用拋出 ClassNotFoundException,NoClassDefFoundError 等錯(cuò)誤。

拋出錯(cuò)誤這種情況還算好,還比較容易定位問(wèn)題。怕就怕,不同版本同一個(gè)類(lèi)內(nèi)部邏輯不同,從而導(dǎo)致業(yè)務(wù)異常。這種問(wèn)題,真的很讓人抓狂,讓人頭禿。 

[[315774]] 

仔細(xì)分析依賴沖突,主要可以分為兩類(lèi):

  • 項(xiàng)目同一依賴應(yīng)用,存在多版本,每個(gè)版本同一個(gè)類(lèi),可能存在差異。
  • 項(xiàng)目不同依賴應(yīng)用,存在包名,類(lèi)名完全一樣的類(lèi)。

下面我們分析一下依賴沖突產(chǎn)生的原因。

1 依賴沖突產(chǎn)生原因

1.1 依賴機(jī)制

Maven 依賴分為兩種情況,直接依賴與間接依賴,這個(gè)比較好理解,大家直接看圖就好。 

 

1.2 仲裁機(jī)制

如果 A 應(yīng)用間接依賴多個(gè) C 應(yīng)用,且版本都不一樣,Maven 將會(huì)通過(guò)仲裁機(jī)制選擇:

  • 優(yōu)先按照依賴管理元素中指定的版本聲明進(jìn)行仲裁時(shí),下面的兩個(gè)原則都無(wú)效了
  • 短路徑優(yōu)先
  • 若路徑相同,將看 pom 中聲明的順序。

第一條原則,我們下面再說(shuō)。

第二條原則,如下圖: 

A 間接依賴兩個(gè)版本 E,這種情況下,由于 A 到 E-1.0 路徑最短,所以 A 中將會(huì)使用 E-1.0。

如果路徑恰好一樣,那么這種情況下 Maven 只能根據(jù) pom 中的順序,選擇最先聲明的,這也是個(gè)無(wú)奈的選擇。

1.3 scope 屬性

Maven 項(xiàng)目可以分為三個(gè)階段:編譯階段,測(cè)試階段,運(yùn)行階段了。通過(guò) scope 屬性,我們可以決定依賴應(yīng)用是否參與以上階段,也將會(huì)影響依賴傳遞。

Maven 提供 6 種 scope :

  • compile
  • provided
  • runtime
  • test
  • system
  • import

compile

compile 是 Maven 默認(rèn)屬性,將會(huì)使依賴包參與項(xiàng)目的編譯,測(cè)試,運(yùn)行階段。當(dāng)然,項(xiàng)目打包之后將會(huì)包含該依賴。

provided

provided 意味著依賴僅參與項(xiàng)目編譯,測(cè)試的階段。若有如下依賴關(guān)系:

A----->B----->C

C 的 scope 為provided,C 將會(huì)參與 B 的編譯,測(cè)試階段,但是 C 不會(huì)傳遞給 A。如果 A 運(yùn)行過(guò)程需要 C,需要自己直接引入 C 依賴。典型如 Servlet API,因?yàn)?Tomcat 等容器內(nèi)部會(huì)提供。

runtime

runtime 代表依賴不再參與項(xiàng)目編譯階段,只參與測(cè)試,運(yùn)行階段。

若依賴不參與編譯階段,這種情況 IDE 中是無(wú)法導(dǎo)入相應(yīng)的類(lèi)的。若存在依賴類(lèi),編譯過(guò)程中將會(huì)報(bào)錯(cuò)。

典型的例子是 JDBC 驅(qū)動(dòng)包,如 mysql :

  1. <dependency> 
  2.     <groupId>mysql</groupId> 
  3.     <artifactId>mysql-connector-java</artifactId> 
  4.     <version>6.0.6</version> 
  5.     <scope>runtime</scope> 
  6. </dependency> 

知識(shí)點(diǎn):這個(gè)好處在于,只能使用 JDBC 標(biāo)準(zhǔn)接口,這樣就不會(huì)與特定的數(shù)據(jù)庫(kù)綁定。后續(xù)若切換數(shù)據(jù)庫(kù),只需要更換 pom,然后修改相應(yīng)的參數(shù)即可。

test

test 僅參與測(cè)試階段的工作,典型的例子為 junit:

  1. <dependency> 
  2.     <groupId>junit</groupId> 
  3.     <artifactId>junit</artifactId> 
  4.     <version>4.12</version> 
  5.     <scope>test</scope> 
  6. </dependency> 

system

system 與 provided 范圍一致,只不過(guò) system 需要使用 systemPath 屬性指定本地路徑,而 provided 將會(huì)從 Maven 倉(cāng)庫(kù)拉取。

import

import 比較特殊,不會(huì)參與以上階段運(yùn)行。其只能在 dependencyManagement下使用,且 type需要為 pom。典型的例子為 Spring-boot 依賴。

  1. <dependencyManagement> 
  2.     <dependencies> 
  3.         <!-- Spring Boot --> 
  4.         <dependency> 
  5.             <groupId>org.springframework.boot</groupId> 
  6.             <artifactId>spring-boot-dependencies</artifactId> 
  7.             <version>2.1.6.RELEASE</version> 
  8.             <type>pom</type> 
  9.             <scope>import</scope> 
  10.         </dependency> 
  11.     </dependencies> 
  12. </dependencyManagement> 

知識(shí)點(diǎn):通過(guò)這種方式,解決單繼承問(wèn)題,也可以更好將依賴分類(lèi)。

另外 Maven scope 將會(huì)影響依賴傳遞。 

如果依賴關(guān)系為: A--->B--->C,A 依賴 B,B 依賴 C。最左列代表 B 的 scope 屬性,第一行代表 C 的 scope 屬性

如上所示,當(dāng) C 的 scope 為 provided/test, C 只在 B 中起作用,不會(huì)通過(guò)間接依賴傳遞給 A。

當(dāng)且僅當(dāng) B 的 scope 為 compile,且 C scope 為 runtime ,A 將會(huì)間接依賴 C,且 scope 為 runtime。其他情況下,C 的 scope 將會(huì)與 B 的 scope 一致。

2. 解決沖突的方法

2.1 使用 Maven 屬性控制依賴傳遞

依賴沖突時(shí),根據(jù)錯(cuò)誤日志,定位到?jīng)_突類(lèi),定位相應(yīng) jar 包,最后通過(guò) excludes 排除相應(yīng)的包。

另外可以結(jié)合 IDEA Maven Helper 插件,主動(dòng)檢查沖突依賴,提前排除。 

 

通過(guò)插件,我們可以清晰看到?jīng)_突包,以及依賴路徑,還有相應(yīng)的 Scope。

除了排除依賴,我們可以通過(guò)合理的設(shè)置 scope 屬性,不讓依賴傳播下去。比如說(shuō),A 需要是使用 Spring-beans 包中某些類(lèi)。如果其他項(xiàng)目鐵定會(huì)使用 Spring,那么我們可以將 A 中 Spring-beansscope 設(shè)置為 provided,讓其他項(xiàng)目自己選擇引入 Spring-beans 的版本。

這個(gè)適合公共基礎(chǔ)包,其他包不要隨便使用provided,若使用一定要寫(xiě)清楚,使用過(guò)程中需要引入的依賴。

以上方法雖然治標(biāo),但是不治本。如果想依賴沖突不發(fā)生,我們需要提前建立一定的規(guī)范,團(tuán)隊(duì)一起遵守,才能有效避免該類(lèi)問(wèn)題。

  1. 應(yīng)用項(xiàng)目中使用 dependencyManagement統(tǒng)一管理基礎(chǔ)依賴,定義統(tǒng)一的版本,如常用中間包,工具包,日志包。
  2. 二方包中不要引入無(wú)關(guān)的依賴,做到盡量少的依賴。團(tuán)隊(duì)開(kāi)發(fā)中,比較常見(jiàn)情況是二方包繼承公共的父 pom,從而導(dǎo)致繼承許多無(wú)相關(guān)的依賴,這種情況可以單獨(dú)管理。
  3. 二方包做好向下兼容,不要隨意改動(dòng)現(xiàn)有類(lèi)名,方法名,字段名。
  4. 項(xiàng)目應(yīng)用上線之前,將 snapshot 替換成正式版本。雖然 snapshot 修改起來(lái)很方便,但是正因?yàn)檫@個(gè)特性,可以被隨便修改。如果某次生產(chǎn)打包發(fā)布不注意,就會(huì)引入。
  5. 二方包不要使用同一個(gè)包名,類(lèi)名。一般來(lái)說(shuō),團(tuán)隊(duì)開(kāi)發(fā)中,包名,類(lèi)名一樣概率比較小。這種比較容易出現(xiàn)在一些重構(gòu)項(xiàng)目,復(fù)制原來(lái)類(lèi),重構(gòu)打包發(fā)布。對(duì)于情況下可以修改包名。如 cmomon-lang3 是 common-lang 升級(jí)版, cmomon-lang3包名為 org.apache.commons.lang3,而 common-lang 包名為 org.apache.commons.lang

3. 總結(jié)

如果我們把 NPE 問(wèn)題當(dāng)做新手村普通怪物,那么依賴沖突問(wèn)題就是人馬這種精英怪。剛開(kāi)始遇到,我們會(huì)被虐的比較慘。只有我們不斷升級(jí),學(xué)習(xí)掌握技巧,然后才能可以從容不迫解決。

ps:塞爾達(dá)中,你們第一次遇見(jiàn)人馬,打了幾次?阿粉記得那天整整從晚上九點(diǎn)打到凌晨?jī)牲c(diǎn),就是打不過(guò)啊~ 

 

4. 幫助文檔

Maven Dependency Scopes

Maven optional關(guān)鍵字透徹圖解

 

責(zé)任編輯:武曉燕 來(lái)源: Java極客技術(shù)
相關(guān)推薦

2015-10-15 09:38:21

程序員發(fā)福

2024-12-13 08:02:55

大模型GPT后端

2019-02-21 10:51:37

程序員技能溝通

2021-05-05 11:32:36

MySQL數(shù)據(jù)庫(kù)索引

2010-05-04 13:52:00

Oracle用戶被鎖

2022-01-05 08:00:00

框架Golang開(kāi)源

2019-11-19 15:10:40

程序員操作系統(tǒng)計(jì)算機(jī)基礎(chǔ)

2015-10-29 10:26:45

Windows管理員技巧Linux

2015-03-09 15:41:08

MongoDB查詢超時(shí)異常Socket Time

2009-07-31 09:14:01

WinCE啟動(dòng)失敗

2017-06-21 08:30:20

MySQL原因解決辦法

2009-11-30 10:09:31

PHP中文亂碼

2023-06-20 11:46:58

2020-12-09 09:30:57

前端開(kāi)發(fā)技術(shù)

2012-06-27 09:11:47

2024-06-14 16:07:41

2012-06-26 10:13:55

2011-04-01 11:16:06

hessian

2024-01-04 09:04:02

2023-10-29 00:55:44

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)