一般来说一个项目有成百上千的 jar 包,当这些 jar 包都存在且不发生冲突的情况下项目才能正常运行;一旦发生冲突可能会出现很多意外的异常及错误信息;所以需要解决的就是规范出一套不会发生 jar包冲突的方案;并在其他项目中直接使用;而不是每个项目都来找出一套方案;一般将找出的不会发生冲突的方案以父工程的形式呈现,每个项目都继承该父工程,在该父工程中使用 dependencyManagement 统一管理这些 jar包。
一般来说,我们自己编写的代码、配置文件报错所导致的问题通常能够在异常信息中看到我们自己写的类的全类名或配置文件的所在路径。如果整个错误信息中完全没有自己编写的部分,全部是框架、第三方工具包里面的类报错,往往就是 jar 包的问题所引起的;主要报错为找不到类或找不到方法。
1、抛异常:找不到类
常见的有:
java.lang.ClassNotFoundException:编译过程中找不到类
java.lang.NoClassDefFoundError:运行过程中找不到类
java.lang.LinkageError:不同类加载器分别加载的多个类有相同的全限定名
比如项目中导入了两个版本的jar包,A版本里面没有 X 类,B版本里面有 X 类,当我们自己写的代码里面没有涉及到 X 类的时候也没影响可以正常运行;一旦涉及到 X 类之后,如果根据maven本身的版本仲裁机制导入的是 B版本倒也正常,如果导入了 A版本则会报错找不到类;或者项目就只导入了A本版。
2、抛异常:找不到方法
java.lang.NoSuchMethodError
程序找不到符合预期的方法;一般多见于通过反射调用方法。
也就是导入的两个版本的 jar包,都含有某个类,但是 A版本中该类不含有 b方法,B版本中该类含有 b方法;那么一旦仲裁导入的是 A版本并且代码中用到了b方法则会报错找不到方法;或者项目就只导入了A版本。
3、没报错但结果不对
这种情况比较典型的原因是:两个 jar 包中的类分别实现了同一个接口,本来是很正常的。但问题在于:由于没有注意命名规范,两个不同实现类恰巧是同一个全限定名称;这样虽然不会报错找不到类或找不到方法,但是结果可能不符合预期。
总结起来就是两种原因导致的:
a、同一jar包的不同版本,最终依赖的 jar包不含有我们用到的类或方法。
b、不同jar包中包含相同全限定名称类,导致预期结果不对。
一般常用框架的整合容易出现的冲突问题,用报错异常搜索都能找到相关问题的jar包;找不到的情况下一般解决方案:
1、把彼此冲突的 jar 包找到。
2、在冲突的 jar 包中选定一个。如果该jar包是通过依赖传递得到的可以通过 exclusions 排除不需要的依赖,或在当前工程中明确声明选定的jar包;
找到冲突的jar包可以使用以下工具:
1、IDEA 的 Maven Helper 插件
该插件是 IDEA 中安装的插件,不是 Maven 插件。可以罗列出来同一个 jar 包的不同版本,以及它们的来源。但是对不同 jar 包中同名的类没有办法。
File->Settings->Plugins->Marketplace->搜索 Maven Helper->点击install安装->安装完毕后重启idea才能生效
效果:
此处根据maven版本仲裁机制中,路径最短优先采用了3.25.0-GA版本,此处没有内容则不存在冲突的版本。
2、Maven 的 enforcer 插件
Maven 的 enforcer 插件既可以检测同一个 jar 包的不同版本,又可以检测不同 jar 包中同名的类。
当前工程 pom.xml 配置:
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-enforcer-pluginartifactId>
<version>1.4.1version>
<executions>
<execution>
<id>enforce-dependenciesid>
<phase>validatephase>
<goals>
<goal>display-infogoal>
<goal>enforcegoal>
goals>
execution>
executions>
<dependencies>
<dependency>
<groupId>org.codehaus.mojogroupId>
<artifactId>extra-enforcer-rulesartifactId>
<version>1.0-beta-4version>
dependency>
dependencies>
<configuration>
<rules>
<banDuplicateClasses>
<findAllDuplicates>truefindAllDuplicates>
banDuplicateClasses>
rules>
configuration>
plugin>
plugins>
pluginManagement>
build>
当前工程执行命令:
mvn clean package enforcer:enforce 或 mvn clean package enforcer:enforce -Dmaven.test.skip=true 跳过测试环节
效果:
每个冲突的包及全限定名称相同的类分开显示的,有多个冲突的包会有多个 Found in及Duplicate classes;如果没有显示内容则没有冲突的包和全限定名称相同的类。
通过以上两种方式确定了有版本冲突的情况下,选择一个需要保留的jar包,然后通过 exclusions 排除不需要的依赖,或在当前工程中明确声明选定的jar包即可;一般情况下解决版本冲突是在项目刚开始构建框架的时候就解决,以免后期开发了很多出现了冲突之后再去解决代价更大。
我们正常在工程中导入的jar包要么是中央仓库的包要么是我们自己开发的包,他们都有一个特点就是生成的这个 jar包都是由maven工程构建而来。但有时候可能我们拿到的某个jar包不是maven工程构建的jar包,此时要想在maven工程中通过 pom.xml 导入依赖可以如下操作。
1、将该 jar 包安装到 Maven 本地仓库中
mvn install:install-file -Dfile=要安装的jar包在本机上的路径 -DgroupId=设置jar包groupId -DartifactId=设置jar包artifactId -Dversion=设置jar包版本 -Dpackaging=jar
如:
mvn install:install-file -Dfile=E:\jar\javajar.jar -DgroupId=com.java.test -DartifactId=javajar -Dversion=1.0 -Dpackaging=jar
执行成功后可以在本地仓库中对应目录下看到生成的 jar 包。
2、在需要使用该jar包的maven工程中引入该依赖;如:
com.java.test
javajar
1.0
Nexus下载安装及对接