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

今天碰到群里小伙伴問,線上程序好像有多個(gè)不同版本的Netty包,怎么去看到底加載了哪一個(gè)?
在說如何看之前,先來說說,當(dāng)你開始意識(shí)到項(xiàng)目里有多個(gè)不同版本的Jar包,都是因?yàn)橛龅搅诉@幾個(gè)異常:
1、java.lang.NoSuchMethodException:自己代碼中調(diào)用了某個(gè)方法,因?yàn)榧虞d了其他版本的jar,這個(gè)版本正好沒這個(gè)方法。 2、java.lang.NoClassDefFoundError:編譯時(shí)候是好的,但是運(yùn)行的時(shí)候,因?yàn)榧虞d的jar版本問題,沒有這個(gè)類。 3、java.lang.ClassNotFoundException:在動(dòng)態(tài)加載某個(gè)Class的時(shí)候,因?yàn)橐虞d的jar不是正確的版本,而導(dǎo)致找不到這個(gè)類。
當(dāng)你在本地運(yùn)行ok,但到服務(wù)器上發(fā)現(xiàn)出現(xiàn)這些錯(cuò)誤的時(shí)候,就要意識(shí)到很可能是jar沖突了(有相同依賴存在多個(gè)版本)。這個(gè)問題往往也會(huì)有這樣的表現(xiàn):多實(shí)例部署的時(shí)候,有的實(shí)例是好的,有的實(shí)例則不行。
查看加載的類和方法
根據(jù)之前分析的異常種類,我們可以去運(yùn)行中的現(xiàn)場確認(rèn)當(dāng)前加載的問題。
這里我們可以使用阿里開源的Arthas工具,如果第一次用,那么按下面操作先安裝再運(yùn)行:
curl?-O?https://arthas.aliyun.com/arthas-boot.jar
java?-jar?arthas-boot.jar
運(yùn)行好之后,會(huì)打印出當(dāng)前運(yùn)行著的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
通過輸入編號(hào)選擇要查看的java應(yīng)用,比如這里選擇:1,進(jìn)入到chapter4-3-0.0.1-SNAPSHOT.jar中去。
下面介紹兩個(gè)重要命令:
第一個(gè):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.
第二個(gè):sm命令,查看具體某個(gè)類有哪些方法。有的版本差異就是去掉了某個(gè)方法,這個(gè)時(shí)候我們就可以通過這個(gè)命令來查看。
比如,通過下面的命令,我們查看一下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)完是加載錯(cuò)誤的情況下,我們要去解決沖突。那么解決沖突要做的就是找到到底哪里沖突了以及我們要去除或者強(qiáng)制
找出版本沖突的方法:使用Maven命令:mvn -U dependency:tree -Dverbose。
命令執(zhí)行之后,會(huì)在控制臺(tái)以樹狀形式列出所有依賴內(nèi)容,然后通過搜索的方式查找沖突的包,看看都是從哪個(gè)依賴中帶進(jìn)來的(在IDEA中搜索會(huì)高亮,更容易找到)。
[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中強(qiáng)制指定要使用的版本,這樣這個(gè)優(yōu)先級(jí)最高,就不會(huì)引入其他版本要帶進(jìn)來的版本了。
好了,今天的分享到這里結(jié)束了,希望對你有所幫助。如果您覺得本文有用,歡迎轉(zhuǎn)發(fā)擴(kuò)散!
往期推薦

