个人遇到的一些jar包冲突的问题分析

    最近项目引入了新的大数据组件,在开发过程遇到了好多次jar的问题,排包排到烦躁,这里真心想说一句IDEA流弊啊!!!

本文对遇到的问题和最近看的maven机制做一个总结,希望以后能尽量避免遇到jar包冲突问题。项目开发工具是IDEA2018.2,Maven3.0.4。

    本文先简单介绍下遇到的Jar包冲突问题以及问题的原因,然后对原因进行解释,这样看的会更加明白一点。最后记录IDEA下的两种打包方式,对开发独立的小工具会有帮助。

 

jar包冲突问题以及引起冲突的原因:

Maven项目中,本人遇到的Jar包冲突问题有如下两种:

  1. 同一个jar包有多个不同版本
  2. 同一个类(类的全限定名完全一样)在不同名的jar包中出现

(“2”遇到过这种情况,代码在本地跑正常,打包成-with-dependencies整包扔到服务器,就跑到其他类里面去了,猜测是和Maven的整包打包机制或者是底层文件系统的顺序有关,具体原因暂时不清楚…)

 

日志中一般会报如下错误:

  1. java.lang.ClassNotFoundException
  2. java.lang.NoSuchMethodError
  3. 没有报错,但是代码跑到其他类里面去了

 

引起上面Jar包冲突本质原因是如下两点:

    1.maven的依赖调解机制(即距离优先和声明优先原则,下文会说到)。

    2.JVM中,classloader只会加载相同的class一次,以后加载的class不会被覆盖!

接下来,本文分别对两种原因进行介绍,如果说的不对,麻烦指正下。

 

Maven的一些机制介绍:

Maven坐标:

    Maven坐标用于唯一标识一个jar包,通过groupId、artifactId、version、packaging、classfier这些元素来定义,前三个是必须的,packaging默认为jar,classfier不太清楚,举个例子:

<dependency>

        <groupId>org.apache.commons</groupId>

        <artifactId>commons-lang3</artifactId>

        <version>3.8.1</version>

     </dependency>

 

依赖范围:

    用于标识jar包在哪个阶段被用到。把第三方jar包放到classpath中,才能调用jar包中的方法,maven把classpath分成三种:

1. compile -- 默认是这种,编译、测试和运行的时候都有效

2. test – 仅在测试时生效

3. runtime – 测试和运行时都有效

 

依赖传递:

加入A依赖与B,B依赖与C,那么在项目A中引用B是,C会自动引进来。例如hbase-server是项目中引入的依赖,hbase-server又依赖了其他jar包,这些jar包会自动导入进来。

 

依赖调解:

maven2.0.8之后引入了依赖调解机制。照搬书上的一个例子,项目A有如下的依赖关系:

  1. A -> B -> C -> X1.0
  2. A -> D -> X2.0

那么最终哪个X会被maven解析使用,maven规定了如下两个原则:

原则1:路径最近优先

原则2:原则1不能解决问题时,Pom文件中第一声明优先

从上面可以看出,X2.0的路径比较短,所以maven会解析X2.0

 

个人觉得,原则1不太是个人能够控制的,但是根据原则2,将log4j之类的jar包声明放在pom文件dependency开头的地方还是蛮有必要的,log4j-jar包冲突日志都打印不出来!

 

可选依赖:

    就是将依赖声明为optional,这样其他项目依赖与本项目时,这些依赖是不会被传递的。

    书上也提到了其实应该避免这种情况,我是没用过,这里也不细究了,以后用到再说。

 

 

Jar包加载顺序介绍:

    项目中,服务最终是运行在tomcat容器中的,所以Jar包的加载顺序和JVM的类加载机制还有Tomcat容器有关。网上看到的Tomcat的jar包加载顺序,对其做了一些修改,加载顺序如下:

1. $java_home/lib 目录下的java核心api (JVM的Bootstrap ClassLoader)

2. $java_home/lib/ext 目录下的java扩展jar包(JVM的Extension ClassLoader)

3. java -classpath/-Djava.class.path所指的目录下的类与jar包(JVM的Application ClassLoader)

4. $CATALINA_HOME/common目录下加载

5. $CATALINA_HOME/server目录下加载

6. $CATALINA_BASE/shared目录下加载

7. 我们的项目路径/WEB-INF/classes下的class文件

8. 我们的项目路径/WEB-INF/lib下的jar文件

(ps:在同一个文件夹下,jar包加载的顺序就和底层文件系统的排序有关了,所以可能出现在windows下没问题,在linux下有问题的情况)

 

如果一定要在同一个项目引用同一个jar包的不同版本,咋办?

    这里说的是类限定名形同的情况。按照JVM的加载机制,classLoader对于同一个class只会加载一次。网上找了找,有两种说法,一种是自己修改jar包源码…;另外一种是自己写个ClassLoader,后续搞一搞,感觉蛮有意思的。

 

 

Jar包冲突如何解决:

    本人一般都是看下报错信息,然后双shift找下类在哪些jar包中出现,然后排掉就好了。IDEA自带了可视化的Maven Tree,非常流弊!

 

 

参考:

《Maven实战》

https://blog.csdn.net/varyall/article/details/81149644(tomcat Jar包加载顺序)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值