Arthas 实战,助你解决同名类依赖冲突问题
上篇文章中,小黑哥分析 Maven 依赖冲突分为两类:
- 项目同一依赖应用,存在多版本,每个版本同一个类,可能存在差异。
- 项目不同依赖应用,存在包名,类名完全一样的类。
第二种情况,往往是这个场景,本地/测试环境运行的都是好好的,上线之后测试就是不行。

这其实与 JVM 类加载有关,本地/测试环境加载正确类,而生产环节加载错的类,为什么会这样?
主要有两个原因:
- 同一个类只会被加载器加载一次
- 不同环境,类的加载顺序不同
同一个类只会被加载器加载一次
JVM 类加载具有缓存机制,每个类加载的时候首先检查一遍,类是否被当前类加载器加载。若未被加载,先交给其父类加载器加载,父类加载器不能加载,才会交给当前类加载器。
当前类加载器加载完成之后,将会将其缓存起来。

类加载的核心源码位于 ClassLoader#loadClass:

① 处将会检查ClassLoader#findLoadedClass 最终将会调用 ClassLoader#findLoadedClass0,这是一个 native 方法,最终将会根据类名加类加载器为键值查找缓存。
每个类加载器负责的加载范围都不一样:
BootstrapClassLoader引导类加载加载最核心的类库,如$JAVA_HOME/jre/lib/ExtClassLoader扩展类加载器负责加载$JAVA_HOME/jre/lib/ext下的一些扩展类AppClassLoader应用类加载器将加载classpath指定的类。
我们运行的应用依赖的各种类,一般将会由 AppClassLoader 记载,同名类被加载后,下次碰到就不会再被加载。
画外音:利用缓存加快查询速度
不同环境,类的加载顺序不同
Java 可以使用 -classpath 参数指定依赖类所在位置。
类的加载顺序可以通过以下方式指定:
java -classpath a.jar:b.jar:c.jar xx.xx.Main
上面这种方式,类加载首先会从 a.jar 中查找相关类,找不到才会继续往后查找。所以可以通过这种方式可以指定使用哪个 jar 包内同名类。
但是这种方式有点繁琐,如果依赖 100 个 jar 包,需要全部写上去。
所以生产环境可以使用使用 shell 命令将 jar 拼接起来:
LIB_DIR=lib
LIB_JARS=`ls $LIB_DIR|grep .jar|awk '{print "'$LIB_DIR'/"$0}'|tr "\n" ":"`
另外 java 支持通配符的写法:
java -classpath './*' xx.xx.Main
这种方式的加载顺序将会受到底层系统文件加载顺序影响。
复现依赖冲突
假设我们现在应用依赖如下:

A 应用依赖 B、C,且 B,C 中存在同包同名类 org.example.App,代码如下:

如果指定 jar 包顺序启动应用:
# A,B,C 放置同一文件夹下
java -classpath A-1.0-SNAPSHOT.jar:B-1.0-SNAPSHOT.jar:C-1.0-SNAPSHOT.jar org.example.ClassA
日志输出如下:

改变 B ,C 顺序:

类加载器的类的查找顺序将会通过 classpath 指定顺序从前往后查找。
如果使用通配符启动:
java -classpath './*' org.example.ClassA
这种情况 jvm 到底加载那个类就成了薛定谔的类了,运行之前无法确定加载类来自哪个 jar 包。

使用 verbose:class 打印加载类
我们可以在 jvm 启动脚本加入如下参数 -verbose:class,然后重启,日志里会打印出每个类的加载信息。
java -verbose:class -classpath './*' xx.xx.Main
日志输出如下:

通过这种方式可以看到加载类来源于哪个Jar包。
不过这种方式需要重启应用,对生产系统来说,影响还是比较大,不太优雅。
Arthas 查到来源类
阿里开源项目 Arthas sc 命令可以用来查找加载类的信息。。
sc 命令是 Search-Class 简写,这个命令能搜索出已经加载到 JVM 中的 Class 信息,支持参数如下表格所示。

程序启动之后,启动 arthas,进入 A 应用。
运行如下命令:
sc -d org.example.App
输出结果如下 :

code-source 显示当前查找类 org.example.App 来自的 C。
另外我们可以 jad 命令反编译类,在线查看源码。

总结
这篇文章主要解释应用中存在多个同名类,环境不同,类加载不同的原因。接着介绍了两种快速查找运行应用依赖类来源的方法。
当定位到了冲突类的来源,我们可以显示指定 classpath jar 包的顺序,指定类加载的顺序。但这只是暂时解决问题。本质上依赖冲突的问题,还是需要深层次排除的。
欢迎关注我的公众号:程序通事,获得日常干货推送。如果您对我的专题内容感兴趣,也可以关注我的博客:studyidea.cn
Arthas 实战,助你解决同名类依赖冲突问题的更多相关文章
- idea解决Maven jar依赖冲突(四)
首先点击右侧的MavenProjects打开以下界面: 这个界面是maven的命令界面: 点击这个图标会进入如下界面: 左上角可以缩放,点击线可以取消冲突依赖,红色线为冲突依赖. 上图为无依赖冲突的s ...
- 解决jar包依赖冲突(idea)
在IDEA状态下查看项目依赖的关系 关系如下图 红色数据jar包冲突 在对应的依赖中出去去冲突依赖
- 使用maven-shade-plugin插件解决spark依赖冲突问题
依赖冲突:NoSuchMethodError,ClassNotFoundException 当用户应用于Spark本身依赖同一个库时可能会发生依赖冲突,导致程序奔溃.依赖冲突表现为在运行中出现No ...
- Maven间接依赖冲突解决办法
如果项目中maven依赖太多,由于还有jar之间的间接依赖,所以可能会存在依赖冲突.依赖冲突大部分都是由于版本冲突引起的,查看maven的依赖关系,可以找到引起冲突的间接依赖 如上图,通过Depend ...
- 通过IDEA快速定位和排除依赖冲突
前言 我们程序员在开发的时候经常会遇到各种各样的 BUG 问题,其中大部分是业务逻辑异常,还有一些是代码书写不规范造成的异常例如:NullPointException(NPE),IndexOutOfB ...
- Maven类包冲突终极三大解决技巧 mvn dependency:tree
Maven对于新手来说是<步步惊心>,因为它包罗万象,博大精深,因为当你初来乍到时,你就像一个进入森林的陌生访客一样迷茫. Maven对于老手来说是<真爱配方>,因为它无所不能 ...
- Spring 循环依赖的三种方式(三级缓存解决Set循环依赖问题)
本篇文章解决以下问题: [1] . Spring循环依赖指的是什么? [2] . Spring能解决哪种情况的循环依赖?不能解决哪种情况? [3] . Spring能解决的循环依赖原理(三级缓存) 一 ...
- Maven 基础(二) | 解决依赖冲突的正确姿势
一.依赖原则 假设,在 JavaMavenService2 模块中,log4j 的版本是 1.2.7,在 JavaMavenService1 模块中,它虽然继承于 JavaMavenService2 ...
- IDEA 解决 Maven 依赖冲突的高能神器,这一篇够不够?
1.何为依赖冲突 Maven是个很好用的依赖管理工具,但是再好的东西也不是完美的.Maven的依赖机制会导致Jar包的冲突.举个例子,现在你的项目中,使用了两个Jar包,分别是A和B.现在A需要依 ...
随机推荐
- nginx常见的面试题
问题1:Nginx是用来干嘛的? Nginx是一个高性能的HTTP和反向代理服务器,这个基本是用来前端服务器集群后做负载均衡和动静分离用的. 负载均衡即是代理服务器将接收的请求均衡的分发到各服务器中, ...
- 跟踪路由(tracert)及ping命令
由于最近学校网络不好,老是有问题,加上最近写了个数据展示系统,要部署到买的域名下,用到了这两个命令 首先,一台服务器,一台工作站,一个笔记本(我的,来测试ip是否通的) 服务器已经部署了三个网站(一个 ...
- python运算符的优先级顺序
最近开始学习python,听大家说python很强大,可以作为脚本语言,支持面向对象.面向过程编程,兼具编译性和解释性的一门动态语言.作为一名程序员有必要掌握这一门强大的"胶水语言" ...
- GpsNet2020 车联网平台
车联网产业是汽车.电子.信息通信.道路交通运输等行业深度融合的新型产业,是全球创新热点和未来发展制高点.车企通过部署车联网系统,为车主提供更好的出行服务体验,增加产品竞争力.依托华为云.边.端协同优势 ...
- 十三、linux-mysql的mysql的核心优化思想
一.数据库运维管理思想核心 1.未雨绸缪,不要停留在制度上,而是要实际做出来 2.亡羊补牢,举一反三,不要好了伤疤忘了疼 3.完善的框架设计及备份.恢复策略 4.定期思考,并实战模拟以上策略演练 二. ...
- SimpleDateFormat 线程安全的解决方案--DateTimeFormatter
SimpleDateFormat并不是线程安全的,因为在SimpleDateFormat中持有一个Calendar类对象在Parse 和Format方法时会调用calendar.setTime(dat ...
- Spring Cloud服务消费者(rest+ribbon)
在上一篇文章,讲了服务的注册和发现.在微服务架构中,业务都会被拆分成一个独立的服务,服务与服务的通讯是基于http restful的.Spring cloud有两种服务调用方式,一种是ribbon+r ...
- 001.前端开发知识,前端基础HTML(2020-01-07)
一.开发工具: chrome . sublime . photoshop 二.Web标准:不是某一个标准,而是由W3C和其他标准化组织制定的一系列标准的集合. 三.HTML的语言语法骨架格式 < ...
- rest framework-restful介绍-长期维护
############### django框架-rest framework ############### # django rest framework 框架 # 为什么学习这个res ...
- 非IoC方式访问Servlet API的两种方法
使用ActionContext访问 ActionContext来自com.opensymphony.xwork2.ActionContext 具体实现代码段如下: <span style=&qu ...