1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

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

        共 3527字,需瀏覽 8分鐘

         ·

        2020-02-21 23:27

        6ad2ff758f25ab3169f42cddb075ab3e.webp

        前言

        依賴沖突是日常開發(fā)中經(jīng)常碰到的過程,如果運(yùn)氣好,并不會(huì)有什么問題。偏偏阿粉有點(diǎn)背,碰到好幾次生產(chǎn)問題,排查一整晚,最后發(fā)現(xiàn)卻是依賴沖突的引起的問題。沒碰到過這個(gè)問題同學(xué)可能沒什么感覺,阿粉舉兩個(gè)最近碰到例子,讓大家感受一些。例子 1:我們公司有個(gè)古老的業(yè)務(wù)基礎(chǔ)包 A。B,C 業(yè)務(wù)依賴這個(gè)包。某個(gè)團(tuán)隊(duì)拷貝 A 的部分代碼進(jìn)行重構(gòu),類名與路徑完全一樣,然后重新打包成 D 發(fā)布。一次業(yè)務(wù)改動(dòng),B 業(yè)務(wù)也引入了 D 包,測(cè)試環(huán)境運(yùn)行的時(shí)候,一切 OK,但是在生產(chǎn)運(yùn)行時(shí),卻拋出?NoSuchMethodError問題原因在于 B 業(yè)務(wù)依賴 A,D。而 A,D 存在兩個(gè)同包同名類,運(yùn)行的時(shí)候,具體加載誰,不同環(huán)境還真不一樣。例子 2:A 業(yè)務(wù)使用?Dubbo?進(jìn)行?RPC調(diào)用,?Dubbo?需要依賴?javassist。當(dāng)前依賴關(guān)系為:
        A------->Dubbo------->javassist-3.18.1.GA
        某次改動(dòng)中引入另外一個(gè)第三方開源包,其依賴?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)行直接失敗。除了上述問題,依賴沖突還可能導(dǎo)致應(yīng)用拋出?ClassNotFoundException,NoClassDefFoundError?等錯(cuò)誤。拋出錯(cuò)誤這種情況還算好,還比較容易定位問題。怕就怕,不同版本同一個(gè)類內(nèi)部邏輯不同,從而導(dǎo)致業(yè)務(wù)異常。這種問題,真的很讓人抓狂,讓人頭禿。a51ed1a6958095e37eb62ffb78568151.webp
        仔細(xì)分析依賴沖突,主要可以分為兩類:
        • 項(xiàng)目同一依賴應(yīng)用,存在多版本,每個(gè)版本同一個(gè)類,可能存在差異。
        • 項(xiàng)目不同依賴應(yīng)用,存在包名,類名完全一樣的類。
        下面我們分析一下依賴沖突產(chǎn)生的原因。


        依賴沖突原因

        1.1 依賴機(jī)制

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

        1.2 仲裁機(jī)制

        如果 A 應(yīng)用間接依賴多個(gè) C 應(yīng)用,且版本都不一樣,Maven 將會(huì)通過仲裁機(jī)制選擇:
        • 優(yōu)先按照依賴管理元素中指定的版本聲明進(jìn)行仲裁時(shí),下面的兩個(gè)原則都無效了
        • 短路徑優(yōu)先
        • 若路徑相同,將看 pom 中聲明的順序。
        第一條原則,我們下面再說。第二條原則,如下圖:b4ebe14944aada4e6b4dc50a72466812.webp
        A 間接依賴兩個(gè)版本 E,這種情況下,由于 A 到 E-1.0 路徑最短,所以 A 中將會(huì)使用 E-1.0。如果路徑恰好一樣,那么這種情況下?Maven?只能根據(jù)?pom?中的順序,選擇最先聲明的,這也是個(gè)無奈的選擇。

        1.3 scope 屬性

        Maven 項(xiàng)目可以分為三個(gè)階段:編譯階段,測(cè)試階段,運(yùn)行階段了。通過?scope?屬性,我們可以決定依賴應(yīng)用是否參與以上階段,也將會(huì)影響依賴傳遞。Maven?提供 6 種?scope?:
        • compile
        • provided
        • runtime
        • test
        • system
        • import
        compilecompile?是?Maven?默認(rèn)屬性,將會(huì)使依賴包參與項(xiàng)目的編譯,測(cè)試,運(yùn)行階段。當(dāng)然,項(xiàng)目打包之后將會(huì)包含該依賴。providedprovided?意味著依賴僅參與項(xiàng)目編譯,測(cè)試的階段。若有如下依賴關(guān)系:
        A----->B----->C
        C 的?scope?為provided,C 將會(huì)參與 B 的編譯,測(cè)試階段,但是 C 不會(huì)傳遞給 A。如果 A 運(yùn)行過程需要 C,需要自己直接引入 C 依賴。典型如?Servlet API,因?yàn)?Tomcat?等容器內(nèi)部會(huì)提供。runtimeruntime?代表依賴不再參與項(xiàng)目編譯階段,只參與測(cè)試,運(yùn)行階段。若依賴不參與編譯階段,這種情況 IDE 中是無法導(dǎo)入相應(yīng)的類的。若存在依賴類,編譯過程中將會(huì)報(bào)錯(cuò)。典型的例子是?JDBC?驅(qū)動(dòng)包,如?mysql?:
            mysql    mysql-connector-java    6.0.6    runtime
        知識(shí)點(diǎn):這個(gè)好處在于,只能使用 JDBC 標(biāo)準(zhǔn)接口,這樣就不會(huì)與特定的數(shù)據(jù)庫(kù)綁定。后續(xù)若切換數(shù)據(jù)庫(kù),只需要更換 pom,然后修改相應(yīng)的參數(shù)即可。testtest?僅參與測(cè)試階段的工作,典型的例子為?junit
            junit    junit    4.12    test
        systemsystem?與?provided?范圍一致,只不過?system?需要使用?systemPath?屬性指定本地路徑,而?provided?將會(huì)從?Maven?倉(cāng)庫(kù)拉取。importimport?比較特殊,不會(huì)參與以上階段運(yùn)行。其只能在?dependencyManagement下使用,且?type需要為?pom。典型的例子為 Spring-boot 依賴。
                                                            org.springframework.boot                spring-boot-dependencies                2.1.6.RELEASE                pom                import                        
        知識(shí)點(diǎn):通過這種方式,解決單繼承問題,也可以更好將依賴分類。另外?Maven scope?將會(huì)影響依賴傳遞。71038e3193687bffc6810604b29bc664.webp如果依賴關(guān)系為:?A--->B--->C,A 依賴 B,B 依賴 C。最左列代表 B 的?scope?屬性,第一行代表 C 的?scope?屬性如上所示,當(dāng) C 的?scope?為?provided/test, C 只在 B 中起作用,不會(huì)通過間接依賴傳遞給 A。當(dāng)且僅當(dāng) B 的?scope?為?compile,且 C?scope?為?runtime?,A 將會(huì)間接依賴 C,且?scope?為?runtime其他情況下,C 的 scope 將會(huì)與 B 的 scope 一致。

        ?

        解決沖突的方法

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

        依賴沖突時(shí),根據(jù)錯(cuò)誤日志,定位到?jīng)_突類,定位相應(yīng)?jar?包,最后通過?excludes?排除相應(yīng)的包。另外可以結(jié)合?IDEA Maven Helper?插件,主動(dòng)檢查沖突依賴,提前排除。ac52a1ba0adce9db959db1ce3638ecb6.webp通過插件,我們可以清晰看到?jīng)_突包,以及依賴路徑,還有相應(yīng)的?Scope。除了排除依賴,我們可以通過合理的設(shè)置?scope?屬性,不讓依賴傳播下去。比如說,A 需要是使用?Spring-beans?包中某些類。如果其他項(xiàng)目鐵定會(huì)使用 Spring,那么我們可以將 A 中?Spring-beansscope?設(shè)置為?provided,讓其他項(xiàng)目自己選擇引入?Spring-beans?的版本。這個(gè)適合公共基礎(chǔ)包,其他包不要隨便使用provided,若使用一定要寫清楚,使用過程中需要引入的依賴。以上方法雖然治標(biāo),但是不治本。如果想依賴沖突不發(fā)生,我們需要提前建立一定的規(guī)范,團(tuán)隊(duì)一起遵守,才能有效避免該類問題。
        1. 應(yīng)用項(xiàng)目中使用?dependencyManagement統(tǒng)一管理基礎(chǔ)依賴,定義統(tǒng)一的版本,如常用中間包,工具包,日志包。
        2. 二方包中不要引入無關(guān)的依賴,做到盡量少的依賴。團(tuán)隊(duì)開發(fā)中,比較常見情況是二方包繼承公共的父 pom,從而導(dǎo)致繼承許多無相關(guān)的依賴,這種情況可以單獨(dú)管理。
        3. 二方包做好向下兼容,不要隨意改動(dòng)現(xiàn)有類名,方法名,字段名。
        4. 項(xiàng)目應(yīng)用上線之前,將?snapshot?替換成正式版本。雖然?snapshot?修改起來很方便,但是正因?yàn)檫@個(gè)特性,可以被隨便修改。如果某次生產(chǎn)打包發(fā)布不注意,就會(huì)引入。
        5. 二方包不要使用同一個(gè)包名,類名。一般來說,團(tuán)隊(duì)開發(fā)中,包名,類名一樣概率比較小。這種比較容易出現(xiàn)在一些重構(gòu)項(xiàng)目,復(fù)制原來類,重構(gòu)打包發(fā)布。對(duì)于情況下可以修改包名。如?cmomon-lang3?是?common-lang?升級(jí)版,?cmomon-lang3包名為 org.apache.commons.lang3,而?common-lang?包名為?org.apache.commons.lang


        總結(jié)

        如果我們把?NPE?問題當(dāng)做新手村普通怪物,那么依賴沖突問題就是人馬這種精英怪。剛開始遇到,我們會(huì)被虐的比較慘。只有我們不斷升級(jí),學(xué)習(xí)掌握技巧,然后才能可以從容不迫解決。ps:塞爾達(dá)中,你們第一次遇見人馬,打了幾次?阿粉記得那天整整從晚上九點(diǎn)打到凌晨?jī)牲c(diǎn),就是打不過啊~
        6ad2ff758f25ab3169f42cddb075ab3e.webp


        幫助文檔

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

        有道無術(shù),術(shù)可成;有術(shù)無道,止于術(shù)

        歡迎大家關(guān)注Java之道公眾號(hào)


        好文章,我在看??

        瀏覽 53
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            久久99久久99| 国产91白丝在一线播放| 国产一区二区成人久久919色| 欧美日韩在线观看中文字幕| 久久久久久无码视频| 91最新在线播放| 国产中文在线| a国产| 白浆AV| 最近2019中文字幕mv第三季歌词 |