Java包管理原則詳解

2020-12-14 計算機java編程

摘要

坊間傳聞java web開發人員寫了那麼多代碼,但是其實一半代碼都在處理NPE。總是在加班,卻大部分時間都在處理包衝突,類加載不了的bug。這些問題總是讓新老程式設計師都很抓狂,有很多的工具可以輔助我們解決這些問題(maven helper插件,arthas等)但是有沒有一些原則可以遵循,在源頭上避免這些問題的發生呢。

問題

經常遇到的問題有ClassNotFoundException通過Class.forName()或者loadClass()方法加載類時,當classpath中又找不到這個類,就會拋這個錯誤。

這個錯誤一般比較好排查,編譯程序時就拋出來了。然後引入對應的jar包,或者刷新classpath就可以解決NoClassDefFoundError類在編譯的時候存在,但是運行的時候不存在。

NoSuchMethodError找不到對應的方法,運行時才會拋錯,這個錯誤在日常開發經常遇到,線上諸多bug都是來源於此。

發生的原因就是多個包依賴了不一樣版本的另外一個包,比如A,B都依賴了C包,A依賴C1,B依賴C2,工程中加載了C1,但是C1中某個類缺少了C2版本的這個類的某個方法,這時候運行時,B依賴的C2方法被調用到了,就會報這個錯。

既然這些問題這麼頭疼,且難以排查,還容易造成線上故障,那平時在開發過程中如何避免這些問題。

做好二方包的管理

重中之中就是做好二方包的管理,第三方包都是公開發布出來的,相對還是比較靠譜的,更多的是二方包的問題。二方包發布的頻率比較高,開發人員的水平又參差不齊,是需要嚴格把控管理的。

線上禁止snapshot包snapshot包是萬惡之源,內容隨時會被人修改,所以線上環境,是絕對不能允許snapshot包存在,否則就是一顆定時炸彈。向下兼容版本號是3位: 主版本號.次版本號.修訂號做了不兼容的更新要升級主版本號。在二方包管理時,儘量不要接口層面的方法,重命名POJO類欄位等不兼容的改動,因為二方包的發布頻次高,版本比較多,維護的代價比較高。很多包發布了幾年,還是0.xx.xx的版本。有不兼容的改動,比如需要搭配版本時候,維護的成本都會讓人奔潰。儘量少的依賴發布出來的二方包儘量少的依賴,二次依賴。對於發布出去的包小心檢查,不要引入不必要的依賴。比如有些業務repo,需要發布rpc-client給其他服務。對外的client與內部代碼通過不同module管理,那麼這時候就不要共用一份parent pom.xml。進行版本管理,因為內部工程使用的和外部不一樣。不同領域的client也可以拆分出來,單獨進行jar包版本管理。changelog發布出來的版本必須要有changelog,做了什麼改動。必須依賴什麼版本的二方包也需要寫清楚。優雅"copy"開源包對於外部開源包,有些需要包裝一下在公司裡面用。切記做好以下兩點記錄下開源版本上做了什麼更改,否則後續人維護的就是一坨屎,不要使用同一個包名,類名。後續別人用開源版本實現時,就會出現不同jar包有相同同一個Class,這時候就不是包衝突解決了,就會出現不確定的加載順序,因為同一個類只會被jvm加載一次,但是加載的是哪個jar的類,就和包依賴的路徑,文件名的順序等有關。出現不同的機器加載的類不一樣,十分難排查。使用包的原則

上面說完了發布包的原則,那麼使用包的原則呢。

1.了解maven仲裁機制

maven仲裁機制就是maven的依賴機制,按順序分別是以下三點

優先按照依賴管理若無版本聲明,則按照「短路徑優先」的原則(Maven2.0)進行仲裁,即選擇依賴樹中路徑最短的版本若路徑長度一致,則按照「第一聲明優先」的原則進行仲裁,即選擇POM中最先聲明的版本2.統一基礎包管理

因為間接依賴有很多的不確定性,所以需要將基礎版本的包統一使用原則1

<dependencyManagement>

來管理,這樣這些包的版本就不會被你無意間引入的某個二方包版本給覆蓋掉。常見的基礎包有中間件的包,日誌,util,序列化等包。

幾個bad case

case 1: 那是我剛來公司的時候,第一個功能上線就翻跟頭了,本地windows環境,測試linux環境都通過測試了。但是上預發的時候出了問題。排查了通宵也沒搞定,差點發布延期。最後問題根源就是「開源包」亂使用的原因。項目中依賴了公司改造的某個mongo client jar包,將類copy過來了,使用了相同的package,但是以不同的jar deploy。我來了以後,在這個工程中使用了開源的mongo client,然後中招了。

case2: A包是一個業務基礎包,非公共基礎包。然後B,C業務依賴A。A包有些設計不合理,一個新來的大刀闊斧的各種重構了,升了一個版本,發布了一個release包出去,完美。然後其他業務方就炸了。因為B使用了新版本,C沒有使用新版本。一個工程中引用了B,C包後,包衝突,就會發生運行時錯誤,NoSuchMethodError 或者NoClassDefFoundError。這時候業務方既不能升A包的版本,也不能降A的版本,可不就炸了嗎。

相關焦點

  • 詳解Java中包(package)的概念,新手也能看懂,內含實例
    那java項目中也是可以分不同文件夾的。只不過java中類的文件夾不叫文件夾,叫「包名」英文單詞:package,那package還有個意思就是打包的意思,所以你可以理解為這個文件夾,就是打了一個包裹,就跟快遞包裹似的,你買了好多東西,不同商家是不同的包裹,每個包裹放的東西是不同類別的。java中也是這個意思,包中放的基本上都是同一類的東西。
  • java_線程池詳解
    java創建線程到底有幾種方式?2、線程池原理創建一個線程,需要一個Thread對象和一個Runable接口實例每次都去new一個Thread對象是很耗費時間的,而且不好管理線程池就是為了復用已存在的Thread對象,防止不停地創建Thread
  • java集合詳解合集
    所以的集合類都位於java.util包下,後來為了處理多線程環境下的並發安全問題,java5還在java.util.concurrent包下提供了一些多線程支持的集合類。在學習Java中的集合類的API、編程原理的時候,我們一定要明白,"集合"是一個很古老的數學概念,它遠遠早於Java的出現。
  • JAVA工具JDK安裝配置詳解
    學習Java,在自己的電腦上安裝Java的開發工具包JDK是必須的。安裝完成後需要進行一系列的手動配置環境,下面介紹如何進行JDK的安裝與配置1.進入JDK的官方網站下載相關的JDK安裝工具包。選擇下載的對應系統的安裝包2.完成下載安裝工具包後,雙擊安裝文件開始按照安裝嚮導進行安裝。3.在安裝到目標文件夾的界面中可以進行安裝到的目錄位置進行修改,可以根據各自的需求是否進行修改。如果無需修改可以採取默認的設置。點擊「下一步」進行安裝。
  • JAVA8 新特性詳解
    Stream 的創建需要指定一個數據源,比如 java.util.Collection的子類,List或者Set, Map不支持。Stream的操作可以串行執行或者並行執行。Java 8擴展了集合類,可以通過 Collection.stream() 或者 Collection.parallelStream() 來創建一個Stream。
  • JAVA 基礎:JAVA開發環境搭建
    一、在windows上安裝jdk下載(略,自行到oracle官網上下載,注意所選系統的版本)2.安裝雙擊jdk安裝包以後進行打開Path變量,在變量值最前加入 %JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;(方法同上)4.驗證:運行cmd,輸入java -version,顯示java版本則成功。
  • Java面向對象之final、abstract抽象、和變量生命周期
    變量生命周期程序中的變量是用來存儲數據的,其又分為常量和變量兩種,關於變量的詳情可以查看我的另一篇文章:Java 變量、表達式和數據類型詳解。package 關鍵字在開發中,一個項目會有成百上千個Java文件,如果所有的Java文件都在一個目錄中,那麼管理起來就會很痛苦,很難想像這樣的項目會是什麼樣子。在Java中,引入了稱之為包(package)的概念。即:關鍵字:package ,專門用來給當前Java文件設置包名(也就是命名空間)。
  • pacebox-springboot 1.1.5 發布,java 生態框架
    案例 inter-boot-demo  boot版demo inter-micro-demo  cloud版demo(nacos+sentinel體系+權限管理+elasticsearch日誌+數據加解密+分布式追蹤(基於opentracing)) inter-boot-generator
  • 使用Java API在Jedis中實現DAO設計模式
    Redis and Jedis在 Windows 上安裝 Redis在 Windows 上安裝 Eclipse創建 Maven 項目創建 SiteDaoRedisImplTest.java,並將其放入 com.example.demo 包中,代碼如下,添加 SiteDao.java 到 com.example.demo.dao 包中,代碼如下,添加 SiteDaoRedisImpl.java 到 com.example.demo.dao 包中,代碼如下,添加 HostPort.java
  • 雲計算核心技術Docker教程:pull/push命令詳解
    >docker pull [OPTIONS] NAME[:TAG|@DIGEST]OPTIONS說明:-a :拉取所有 tagged 鏡像disable-content-trust :忽略鏡像的校驗,默認開啟例如,從Docker Hub下載java
  • 開發崗位這麼多,為什麼選Java?你學Java了嗎-開課吧
    提到C++語言,很多人發現在使用過程中最容易出現的錯誤就是內存管理,而java有自動垃圾回收器,不用擔心內存。java工程師工資一般多少?java自學容易嗎?java難學嗎?學java要學多久-開課吧零基礎學java難麼?
  • 詳解SMART原則,完成行動目標確定
    人生理想目標是一個比較概括性地目標,可以不是具體的,但是行動目標必須是可以具體的,可以達成的,務必遵循SMART原則。SMART原則詳解:——S代表具體(Specific),指個人行動或者組織績效要切中特定的行為,要制定到動作上面,不能籠統;——M代表可量化(Measurable),指個人行動或者組織績效指標是可以進行量化的,驗證這些目標達成效果的數據或者信息是可以獲得的;
  • 你必須掌握的 21 個 Java 核心技術!
    這個知識點是最最基本的java開發者需要掌握的,第一個肯定是教你如何在命令行中執行java程序,但是很多人一旦把java學完了,IDE用上了,就把這個都忘了。>java程序涉及到的各個路徑(classpath, java。
  • Java之final關鍵字詳解
    代碼:每日一題下面有關java代碼安全性的敘述哪些是對的?A、字節碼校驗器加載查詢執行需要的所有的類。B、運行時解釋器執行代碼。C、在運行時,字節碼被加載,驗證然後在解釋器裡面去運行。D、類加載器通過分離本機文件系統的類和從網絡導入的類增加安全性。
  • java、php、python誰更容易學習呢?
    就市場前景而言,三者都有著廣闊的就業前景,薪資也都很不錯,java、php、python誰更容易學習呢?今天千鋒就從學習路線方面給大家分析一下。如果你選擇學習java,需要經歷四大階段。一階段為java基礎,包括java的基本介紹、數組、面向對象、類、線程、接口、反射等知識;二階段為JavaWeb部分,包括HTML、CSS3、JavaScript、MySQL資料庫、JSP、Ajax、JDBC連接池、jQuery等;三階段為java框架學習,包括Maven、Hibernate、Spring、Spring MVC、MyBatis、WebService、SVN、Activiti等;四階段為
  • Java基礎面試題簡單總結
    Int是java的原始數據類型,Integer是java為int提供的封裝類。Java為每個原始類型提供了封裝類。引用類型和原始類型具有不同的特徵和用法,它們包括:大小和速度問題,這種類型以哪種類型的數據結構存儲,當引用類型和原始類型用作某個類的實例數據時所指定的預設值。
  • 哪裡有賣Java語言的虛擬主機
    Java語言的虛擬主機,就是支持java技術的虛擬主機。java語言具有功能強大和簡單易用的特徵,java作為主機中的大眾化流行語言,已經有很多用戶在使用。Java雖然非常流行,而且從業人員收入高,但真正使用Java環境來搭建伺服器的,卻比較少。需求少而且成本高,導致很多服務商不願意投入Java虛擬主機。目前提供java虛擬主機的服務商比較少,其中比較知名的是西部數碼。
  • 私人收藏:這十大java開源庫深得人心!
    很多人低估或不完全了解Java標準庫,並且不知道如何在編程時釋放其全部功能,或者根本不使用,以下是一些庫的簡要說明:  ·java.lang總是被隱含導入,因為它包含基本無法編程的所有內容,不需要String,Double,Enum,Math等。
  • python和java哪個好
    python和java哪個好?書聲琅琅Python培訓老師介紹,這兩年來IT教育發展迅速,程式語言隨著發展也日益月異,比較火的Python語言發展迅速,與傳統使用的JAVA語言來說,火爆程度差不多,就有同學問了,python和java這兩個語言到底哪個好呢?
  • 一行JAVA代碼如何運行起來?
    JVM運行Java程序有兩種方式,分別是jar包和Class類文件,jar包是偏上層的方式,把所有程序都打包成一個jar包,便於交付測試人員測試、運維人員發布,它的運行邏輯是通過java.exe找到java自帶的GetMainClassName函數,該函數獲取JNIENV實例,並調用JarFileJNIENV實例中的GetMainfest()函數獲取MainClass函數,Main函數再調用Java.c