JAR沖突問題的解決以及運行狀態(tài)下如何查看加載的類

今天碰到群里小伙伴問,線上程序好像有多個不同版本的Netty包,怎么去看到底加載了哪一個?
在說如何看之前,先來說說,當(dāng)你開始意識到項目里有多個不同版本的Jar包,都是因為遇到了這幾個異常:
1、java.lang.NoSuchMethodException:自己代碼中調(diào)用了某個方法,因為加載了其他版本的jar,這個版本正好沒這個方法。 2、java.lang.NoClassDefFoundError:編譯時候是好的,但是運行的時候,因為加載的jar版本問題,沒有這個類。 3、java.lang.ClassNotFoundException:在動態(tài)加載某個Class的時候,因為要加載的jar不是正確的版本,而導(dǎo)致找不到這個類。
當(dāng)你在本地運行ok,但到服務(wù)器上發(fā)現(xiàn)出現(xiàn)這些錯誤的時候,就要意識到很可能是jar沖突了(有相同依賴存在多個版本)。這個問題往往也會有這樣的表現(xiàn):多實例部署的時候,有的實例是好的,有的實例則不行。
查看加載的類和方法
根據(jù)之前分析的異常種類,我們可以去運行中的現(xiàn)場確認(rèn)當(dāng)前加載的問題。
這里我們可以使用阿里開源的Arthas工具,如果第一次用,那么按下面操作先安裝再運行:
curl?-O?https://arthas.aliyun.com/arthas-boot.jar
java?-jar?arthas-boot.jar
運行好之后,會打印出當(dāng)前運行著的java應(yīng)用,比如:
[INFO]?arthas-boot?version:?3.4.6
[INFO]?Process?40611?already?using?port?3658
[INFO]?Process?40611?already?using?port?8563
[INFO]?Found?existing?java?process,?please?choose?one?and?input?the?serial?number?of?the?process,?eg?:?1.?Then?hit?ENTER.
*?[1]:?40611?chapter4-3-0.0.1-SNAPSHOT.jar
??[2]:?37786
通過輸入編號選擇要查看的java應(yīng)用,比如這里選擇:1,進(jìn)入到chapter4-3-0.0.1-SNAPSHOT.jar中去。
下面介紹兩個重要命令:
第一個:sc命令,我們確認(rèn)一下可能沖突的jar包下面,是否有對應(yīng)的class。有些不同版本包下class就不一樣,馬上就可以分辨出來。
比如,通過下面的命令,我們查看一下com.didispace包下有什么類:
[arthas@40611]$?sc?com.didispace.*
com.didispace.chapter43.Chapter43Application
com.didispace.chapter43.Chapter43Application$$EnhancerBySpringCGLIB$$8b82b194
com.didispace.chapter43.UploadController
Affect(row-cnt:3)?cost?in?6?ms.
第二個:sm命令,查看具體某個類有哪些方法。有的版本差異就是去掉了某個方法,這個時候我們就可以通過這個命令來查看。
比如,通過下面的命令,我們查看一下com.didispace.chapter43.UploadController類下有些什么方法:
[arthas@40611]$?sm?com.didispace.chapter43.UploadController
com.didispace.chapter43.UploadController?()V
com.didispace.chapter43.UploadController?create(Lorg/springframework/web/multipart/MultipartFile;)Ljava/lang/String;
com.didispace.chapter43.UploadController?uploadPage()Ljava/lang/String;
Affect(row-cnt:3)?cost?in?5?ms.
找到?jīng)_突并解決沖突
在確認(rèn)完是加載錯誤的情況下,我們要去解決沖突。那么解決沖突要做的就是找到到底哪里沖突了以及我們要去除或者強制
找出版本沖突的方法:使用Maven命令:mvn -U dependency:tree -Dverbose。
命令執(zhí)行之后,會在控制臺以樹狀形式列出所有依賴內(nèi)容,然后通過搜索的方式查找沖突的包,看看都是從哪個依賴中帶進(jìn)來的(在IDEA中搜索會高亮,更容易找到)。
[INFO]?com.didispace:chapter4-3:jar:0.0.1-SNAPSHOT
[INFO]?+-?org.springframework.boot:spring-boot-starter-web:jar:2.4.1:compile
[INFO]?|??+-?org.springframework.boot:spring-boot-starter:jar:2.4.1:compile
[INFO]?|??|??+-?org.springframework.boot:spring-boot:jar:2.4.1:compile
[INFO]?|??|??+-?org.springframework.boot:spring-boot-autoconfigure:jar:2.4.1:compile
[INFO]?|??|??+-?org.springframework.boot:spring-boot-starter-logging:jar:2.4.1:compile
[INFO]?|??|??|??+-?ch.qos.logback:logback-classic:jar:1.2.3:compile
[INFO]?|??|??|??|??\-?ch.qos.logback:logback-core:jar:1.2.3:compile
[INFO]?|??|??|??+-?org.apache.logging.log4j:log4j-to-slf4j:jar:2.13.3:compile
[INFO]?|??|??|??|??\-?org.apache.logging.log4j:log4j-api:jar:2.13.3:compile
[INFO]?|??|??|??\-?org.slf4j:jul-to-slf4j:jar:1.7.30:compile
[INFO]?|??|??+-?jakarta.annotation:jakarta.annotation-api:jar:1.3.5:compile
[INFO]?|??|??\-?org.yaml:snakeyaml:jar:1.27:compile
[INFO]?|??+-?org.springframework.boot:spring-boot-starter-json:jar:2.4.1:compile
[INFO]?|??|??+-?com.fasterxml.jackson.core:jackson-databind:jar:2.11.3:compile
[INFO]?|??|??|??+-?com.fasterxml.jackson.core:jackson-annotations:jar:2.11.3:compile
[INFO]?|??|??|??\-?com.fasterxml.jackson.core:jackson-core:jar:2.11.3:compile
[INFO]?|??|??+-?com.fasterxml.jackson.datatype:jackson-datatype-jdk8:jar:2.11.3:compile
[INFO]?|??|??+-?com.fasterxml.jackson.datatype:jackson-datatype-jsr310:jar:2.11.3:compile
[INFO]?|??|??\-?com.fasterxml.jackson.module:jackson-module-parameter-names:jar:2.11.3:compile
[INFO]?|??+-?org.springframework.boot:spring-boot-starter-tomcat:jar:2.4.1:compile
[INFO]?|??|??+-?org.apache.tomcat.embed:tomcat-embed-core:jar:9.0.41:compile
[INFO]?|??|??+-?org.glassfish:jakarta.el:jar:3.0.3:compile
[INFO]?|??|??\-?org.apache.tomcat.embed:tomcat-embed-websocket:jar:9.0.41:compile
[INFO]?|??+-?org.springframework:spring-web:jar:5.3.2:compile
[INFO]?|??|??\-?org.springframework:spring-beans:jar:5.3.2:compile
[INFO]?|??\-?org.springframework:spring-webmvc:jar:5.3.2:compile
[INFO]?|?????+-?org.springframework:spring-aop:jar:5.3.2:compile
[INFO]?|?????+-?org.springframework:spring-context:jar:5.3.2:compile
[INFO]?|?????\-?org.springframework:spring-expression:jar:5.3.2:compile
[INFO]?+-?org.springframework.boot:spring-boot-starter-thymeleaf:jar:2.4.1:compile
[INFO]?|??+-?org.thymeleaf:thymeleaf-spring5:jar:3.0.11.RELEASE:compile
[INFO]?|??|??+-?org.thymeleaf:thymeleaf:jar:3.0.11.RELEASE:compile
[INFO]?|??|??|??+-?org.attoparser:attoparser:jar:2.0.5.RELEASE:compile
解決版本沖突的方式主要兩種:
1、通過上面的命令找到不需要的版本之后,在引入的依賴中,使用exclusions將其排除,比如下面這樣:
<dependency>
????????<groupId>org.springframework.bootgroupId>
????????<artifactId>spring-boot-starter-jdbcartifactId>
????????<exclusions>
????????????????<exclusion>
????????????????????????<groupId>xxxgroupId>
????????????????????????<artifactId>yyyartifactId>
????????????????exclusion>
????????exclusions>dependency>
2、在pom.xml中強制指定要使用的版本,這樣這個優(yōu)先級最高,就不會引入其他版本要帶進(jìn)來的版本了。
好了,今天的分享到這里結(jié)束了,希望對你有所幫助。如果您覺得本文有用,歡迎轉(zhuǎn)發(fā)擴散!
往期推薦

