上篇文章中,小黑哥分析 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 实战,助你解决同名类依赖冲突问题相关推荐

  1. Spring Boot 解决同名类导致的bean名冲突bean name conflicts

    问题描述 项目中有两个同名类都叫MyService,只是放在不同的包名下 package com.yeyuanxinyi; @Service() public class MyService {} p ...

  2. 一次Maven依赖冲突采坑,把依赖调解、类加载彻底整明白了

    今年年初的时候,阅读过<Maven实战>,当时有了解到Maven可以依赖调解,即当包版本不一致时,会根据一定规则选择相应的包来加载,从而避免冲突.当时不解的是既然Maven都能解决冲突,为 ...

  3. Maven 依赖冲突踩坑后,将依赖调解、类加载彻底整明白了

    作者 | 草捏子 来源 | 草捏子(ID:chaycao) 头图 |  CSDN 下载自东方IC 今年年初的时候,阅读过<Maven实战>,当时有了解到Maven可以依赖调解,即当包版本不 ...

  4. 使用maven-shade-plugin插件解决spark依赖冲突问题

    (尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/54973182冷血之心的博客) 依赖冲突:NoSuchMethodE ...

  5. 依赖冲突了如何解决?

    点击上方关注 "终端研发部" 设为"星标",和你一起掌握更多数据库知识 作者 | jajian 链接 | cnblogs.com/jajian/p/110025 ...

  6. idea解决maven依赖冲突

    最近在一个老spring项目上面需要适配一个新功能,过程中也需要升级spring的版本,然后最后就导致maven的依赖包很乱,同一个包存在多个版本,本文就记录下使用idea解决maven的依赖冲突,同 ...

  7. 排除jar_通过IDEA快速定位和排除依赖冲突

    作者:jajianhttp://cnblogs.com/jajian/p/11002521.html 推荐阅读(点击即可跳转阅读) 1. SpringBoot内容聚合 2. 面试题内容聚合 3. 设计 ...

  8. c++中两个头文件定义同名类的解决办法

    今天考虑了一个问题,如果两个头文件比如time.h times.h里面都定义了一个time的类,要怎么解决?vs编译器只对cpp文件进行编译,在编译阶段,这两个头文件的实现文件都不会出错,如果不在主函 ...

  9. 程序员需要了解依赖冲突的原因以及解决办法

    前言 依赖冲突是日常开发中经常碰到的过程,如果运气好,并不会有什么问题.偏偏阿粉有点背,碰到好几次生产问题,排查一整晚,最后发现却是依赖冲突的引起的问题. 没碰到过这个问题同学可能没什么感觉,阿粉举两 ...

  10. Jar 包依赖冲突排查思路和解决方法

    作者:jingQ https://www.sevenyuan.cn/ 起因 应用从 jdk7 升级到 jdk8,终于可以用上新特性的语法进行代码编写,通过几轮开发.测试和验证后,在上预发环境时,应用突 ...

最新文章

  1. C#读取AD域用户信息
  2. Android提高十六篇之使用NDK把彩图转换灰度图
  3. sklearn 随机分割数据_sklearn.ensemble.RandomForestClassifier 随机深林参数详解
  4. windows文件服务器双机热备_遇到ZFS文件系统如此棘手的问题,这种办法简单又高效!...
  5. MFC六大核心机制之四:永久保存(串行化)
  6. 什么样的人贷款更容易?
  7. NET Core微服务之路:实战SkyWalking+Exceptionless体验生产下追踪系统
  8. Egret 之 消除游戏 开发 PART 6 Egret elimination game development PART 6
  9. vue+element+node构建单片机控制系统
  10. 什么是无服务器架构,你理解对了吗?
  11. 程序员:走路也可以帮你拥有完美的翘臀!
  12. c语言把char转化为string,浅析string 与char* char[]之间的转换
  13. php 触控精灵,背景控制,精灵图 3-12
  14. HyperMesh快捷键、鼠标操作、键盘操作
  15. RFID手持机助力仓储物流信息化管理
  16. 嵌入式远程机房数采监控报警系统
  17. 两大权威机构点赞百度AI,“技术为王”时代的中国旗帜
  18. 微信测试号的开发——网页授权
  19. IM软件客户端,使用状态机来保持在线状态
  20. 养猫日记2022120

热门文章

  1. (1)-(Two SUM-在数组中找到两个数,他们的和为给定的数)-(数组遍历)
  2. poj1716 Integer Intervals(差分约束)
  3. 【距离GDOI:136天】 后缀数组中...
  4. python openoffice_使用Python创建openoffice .odt文档
  5. 白话使用zookeeper作为注册中心搭建微服务
  6. 时间序列数据建模分析利器sktime初体验
  7. python实现给定一个列表计数指定数字出现的所有次数
  8. 2021-08-04 Mysql联表查询
  9. 线程的常用方法(1)
  10. json文件存储 qt_QT开发(五十一)——QtQuick基础