jar包冲突组建设计书

京东云开发者
• 阅读 210

. 背景

实际开发过程中,使用maven管理jar给我们开发带来了很多便利,不需要自己一个一个的jar包下载了,只需要配置个pom配置文件就可以了,写上对应坐标和仓库地址就可以了。但是jar冲突没问题没有解决,有冲突的jar包maven不会给我们检查出来还是会根据我们的配置进行下载,等到编译才会报错,并且报错信息很晦涩,需要面向百度查一会可能才能定位出问题。

这时候我们迫切需要有个东东可以提前告诉我们我的工程里有内奸,需要及时剔除,否则会影响军心。 2. 功能设计

1、maven插件,通过参数指令传给插件

2、工程里所有jar包冲突预警

3、工程中不能共存jar预警

4、自定义规则预警 3. 功能技术方案

3.1 详细技术方案

1、maven插件开发,继承AbstractMojo,入口方法execute,命令参数可以通过注解方式传入,例如:@Parameter(property = "groupId"),边亮可以获取groupId对应传入的参数值。

2、获取工程中所有的jar包及类:通过Maven获取MavenProject,拿到工程后就可以拿到所有的jar及其所有依赖树,把所有jar包放到一个大map中。

3、jar包冲突算法:

A、相同jar包版本冲突:通过自定义规则配置到属性文件中,定义某jar的哪些版本水火不相容。规则示例:xxxgroupId:xxxArtifactId>5.1.8,那么程序则认为这个坐标的jar包大于我要发(5.1.8)时就是内奸,需要除掉。

B、相同jar包版本冲突:俩jar包中有class个数不同或者个数相同但是MD5后结果不同或者俩jar包中有相同类大小不同这三种情况认为此山不容俩jar包。具体代码此处省略一万行。

C、不相同jar包冲突:通过自定义规则配置到属性文件中,定义某jar的某个版本和别的某jar包某个版本互看不顺眼。规则示例:if 王宝强groupId:王宝强ArtifactId >=2.2.2 then 经纪人groupId:经纪人ArtifactId>3.3.3,此规则代表王宝强jar包版本222与经纪人jar包版本333不能共存,否则会产生绿帽子,给出绿帽告警。

已知的会产生绿帽告警的有如下:

log4j-over-slf4j 和 slf4j-log4j12 不能共存。

jcl-over-slf4j 和 slf4j-jcl 不能共存。

jcl-over-slf4j 和 commons-logging 不能共存。

出处:https://www.slf4j.org/codes.html#version_mismatch。



部分核心代码展示

private void execute() throws MojoExecutionException { this.getLog().info("FindConflicts is working..."); // preparing before collecting classes & artifacts LogConflictsCollector logDepCollector = new LogConflictsCollector(); VersionConflictCollector versionConflictCollector = new VersionConflictCollector(); if (versionCheckConfig != null) { try { versionConflictCollector.init(versionCheckConfig); } catch (FileNotFoundException e) { this.getLog().info("versionCheckConfig:" + versionCheckConfig + " doesn't exist."); } catch (Exception e) { } } Set groupIdsToCheck = null; if (groupId != null) { String[] a = groupId.split(","); if (a.length > 0) { groupIdsToCheck = new HashSet(); for (int i = 0; i < a.length; i++) { groupIdsToCheck.add(a[i].trim()); } } } Set artifactIdsToCheck = null; if (artifactId != null) { String[] a = artifactId.split(","); if (a.length > 0) { artifactIdsToCheck = new HashSet(); for (int i = 0; i < a.length; i++) { artifactIdsToCheck.add(a[i].trim()); } } } int totalJarNum = 0; int totalClassNum = 0; // key:the id of an artifact, value:the classNum Map<String, Integer> totalClassNumMap = new HashMap<String, Integer>(); // data is used to store the the information of class, key: the className , value is the class information of its. Map<String, List> data = new HashMap<String, List>(); // get the final artifacts Set artifacts = this.getProject().getArtifacts(); for (Iterator iterator = artifacts.iterator(); iterator.hasNext();) { Artifact artifact = (Artifact) iterator.next(); if (!artifact.isOptional()) { if ("jar".equals(artifact.getType())) { if (groupIdsToCheck != null && !groupIdsToCheck.contains(artifact.getGroupId())) { continue; } if (artifactIdsToCheck != null && !artifactIdsToCheck.contains(artifact.getArtifactId())) { continue; } totalJarNum++; ArtifactWrapper artifactWrapper = new ArtifactWrapper(); artifactWrapper.artifact = artifact; artifactWrapper.originFrom = this.getOriginFrom(artifact); logDepCollector.collect(artifactWrapper); versionConflictCollector.collect(artifactWrapper); JarFile jf; try { jf = new JarFile(artifact.getFile()); Enumeration jfs = jf.entries(); while (jfs.hasMoreElements()) { JarEntry jfn = jfs.nextElement(); String fileName = jfn.getName(); if (fileName.endsWith(".class")) { // ignore inner class 忽略内部类 if (fileName.indexOf("$") == -1) { ClzWrapper clzWrapper = new ClzWrapper(); clzWrapper.className = fileName; clzWrapper.artifactWrapper = artifactWrapper; clzWrapper.size = jfn.getSize(); if (data.get(fileName) == null) { List clzInfos = new ArrayList(); clzInfos.add(clzWrapper); data.put(fileName, clzInfos); } else { data.get(fileName).add(clzWrapper); } logDepCollector.collect(clzWrapper); String id = Util.getId(artifact); if (totalClassNumMap.get(id) == null) { totalClassNumMap.put(id, 1); } else { totalClassNumMap.put(id, totalClassNumMap.get(id) + 1); } totalClassNum++; } } } } catch (IOException e) { e.printStackTrace(); } } } } // iterator each conflicts 迭代器每次冲突 Set totalConflictJarNum = new HashSet(); int totalConflictClassNum = 0; Set set = data.keySet(); List list = new ArrayList(set); Collections.sort(list, new ClassConflictsComparator()); Iterator iter = list.iterator(); List jarConflictGroups = new ArrayList(); Set jarConflictGroupKeys = new HashSet(); // key:jarConflictsGroupKey, value:conflitsClassNum Map<String, Integer> jarConglictGroupConflitsClassNumMap = new HashMap<String, Integer>(); List classConflicts = new ArrayList(); int classConflictNum = 1; while (iter.hasNext()) { String className = (String) iter.next(); List clzInfos = data.get(className); if (clzInfos.size() == 1) { // no conflicts continue; } long clzSize = clzInfos.get(0).size; boolean isConflicts = false; // only conflicts if the size of class is not equal, for (Iterator iterator = clzInfos.iterator(); iterator.hasNext();) { ClzWrapper clzInfo = (ClzWrapper) iterator.next(); if (clzInfo.size != clzSize) { isConflicts = true; break; } } if (isConflicts) { JarConflictGroup jarConflictGroup = new JarConflictGroup(); for (Iterator iterator = clzInfos.iterator(); iterator.hasNext();) { ClzWrapper clzInfo = (ClzWrapper) iterator.next(); // jar conflicts jarConflictGroup.add(clzInfo.artifactWrapper); totalConflictJarNum.add(Util.getId(clzInfo.artifactWrapper.artifact)); // class conflicts ClassConflict classConflict = new ClassConflict(); classConflict.setClassName(clzInfo.className); classConflict.setGroupId(clzInfo.artifactWrapper.artifact.getGroupId()); classConflict.setArtifactId(clzInfo.artifactWrapper.artifact.getArtifactId()); classConflict.setVersion(clzInfo.artifactWrapper.artifact.getVersion()); classConflict.setOriginFrom(Util.formatOriginFrom(clzInfo.artifactWrapper.originFrom)); classConflict.setNumber(classConflictNum); classConflicts.add(classConflict); totalConflictClassNum++; } classConflictNum++; String jarConflictsGroupKey = jarConflictGroup.getGroupKey(); if (jarConglictGroupConflitsClassNumMap.get(jarConflictsGroupKey) == null) { jarConglictGroupConflitsClassNumMap.put(jarConflictsGroupKey, clzInfos.size()); } else { jarConglictGroupConflitsClassNumMap.put(jarConflictsGroupKey, clzInfos.size() + jarConglictGroupConflitsClassNumMap.get(jarConflictsGroupKey)); } if (!jarConflictGroupKeys.contains(jarConflictsGroupKey)) { jarConflictGroupKeys.add(jarConflictsGroupKey); jarConflictGroups.add(jarConflictGroup); } } } // jarConflicts for (Iterator iterator = jarConflictGroups.iterator(); iterator.hasNext();) { JarConflictGroup jarConflictGroup = (JarConflictGroup) iterator.next(); jarConflictGroup.setConflitsClassNum(jarConglictGroupConflitsClassNumMap.get(jarConflictGroup.getGroupKey())); int groupTotalClass = 0; List artifactWrappers = jarConflictGroup.getArtifactWrappers(); if (artifactWrappers != null && artifactWrappers.size() > 0) { for (Iterator iterator_1 = artifactWrappers.iterator(); iterator_1.hasNext();) { ArtifactWrapper artifactWrapper = (ArtifactWrapper) iterator_1.next(); Artifact artifact = artifactWrapper.artifact; groupTotalClass += totalClassNumMap.get(Util.getId(artifact)); } jarConflictGroup.setTotalClassNum(groupTotalClass); } } if (jarConflictGroups.size() > 0) { Collections.sort(jarConflictGroups, new JarConflictGroupComparator()); int number = 1; List jarConflicts = new ArrayList(); for (Iterator iterator = jarConflictGroups.iterator(); iterator.hasNext();) { JarConflictGroup jarConflictGroup = (JarConflictGroup) iterator.next(); jarConflictGroup.setNumber(number++); jarConflicts.addAll(jarConflictGroup.getJarConflicts()); } this.getLog().warn("*****Jar Conflicts****"); this.getLog().warn((new TableGenerator()).generateTable(jarConflicts)); this.getLog().info("Jar Conflicts Total: jar conflicts ratio:" + totalConflictJarNum.size() + "/" + totalJarNum + "=" + NumberFormat.getPercentInstance().format((float) totalConflictJarNum.size() / totalJarNum)); this.getLog().info("Jar Conflicts Solution Hint: choose one artifact of the conflicts, and exclude other artifacts at pom.xml according to originFrom."); } else { this.getLog().info("No jar conflicts found!"); } if (showClassConflicts) { if (classConflicts.size() > 0) { this.getLog().warn("*****Class Conflicts****"); this.getLog().warn((new TableGenerator()).generateTable(classConflicts)); this.getLog().info("Class Conflicts Total: class conflicts ratio:" + totalConflictClassNum + "/" + totalClassNum + "=" + NumberFormat.getPercentInstance().format((float) totalConflictClassNum / totalClassNum)); this.getLog().info("Class Conflicts Solution Hint: choose one artifact of the conflicts, and exclude other artifacts at pom.xml according to originFrom."); } else { this.getLog().info("No class conflicts found!"); } } List logConflicts = logDepCollector.getLogConflicts(); if (logConflicts != null && logConflicts.size() > 0) { this.getLog().warn("*****Log Conflicts****"); this.getLog().warn((new TableGenerator()).generateTable(logConflicts)); this.getLog().info("Log Conflicts Solution Hint: choose one artifact of the conflicts, and exclude other artifacts at pom.xml according to originFrom."); this.getLog().info("As for the conflicts of SLF4J, you can refer to this offical article:https://www.slf4j.org/codes.html#version_mismatch"); } else { this.getLog().info("No log conflicts found!"); }

List versionConflicts = versionConflictCollector.getVersionConflict(); if (versionConflicts != null && versionConflicts.size() > 0) { this.getLog().warn("*****Version Conflicts****"); this.getLog().warn((new TableGenerator()).generateTable(versionConflicts)); this.getLog().info("Version Conflicts Solution Hint: update the version of the artifact according to requiredVersion"); } else { this.getLog().info("No version conflicts found!"); } this.getLog().info("FindConflicts finished!");

}



点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
3年前
SSM 框架
SSM框架04使用maven创建web项目<p<spanstyle"fontsize:14px;"&nbsp;&nbsp;&nbsp;&nbsp;本篇介绍使用MAVEN来管理jar包,就不用一个一个去添加和下载jar包了,直接在maven配置文件中配置就可以了,maven可以帮助我们自动下载。<spanstyle"co
kenx kenx
3年前
Maven 基础标签之版本管理和冲突解决
前言我们在做java项目的时候由于jar包太多,我们就需要使用maven做项目管理,管理项目的jar包依赖,包括打包上线maven基础Maven是一个项目管理工具,主要用于项目构建,依赖管理,项目信息管理每个maven项目根目录都会有一个pom.xml文件,负责项目构建,依赖管理在这个文件里面,你只需要添加相应的jar包坐标配置,maven就会自动
Stella981 Stella981
3年前
Maven项目使用打包时使用本地jar包库
在使用maven管理项目时,有时候我们可能会使用一些第三方的jar包依赖库,但是这些jar包依赖库又没有在共有的maven仓库。通常只能下来放到本项目的lib目录下。但是我们打包时如果不做处理,那么打包后的fatjar中不会有lib文件夹中的相关jar包。打包后无法运行起来,因此需要做特殊处理,让maven打包时能够把使用到外部jar打进去。主要就是在
Stella981 Stella981
3年前
Maven 项目下slf4j 包冲突问题
今天遇到Maven下Jar包冲突问题.由于Mavenjar包是自动依赖..但是jar包依赖的版本不一样..会造成冲突就比如遇到:org.slf4j.spi.LocationAwareLogger.log(Lorg/slf4j/Marker;Ljava/lang/String;ILjava/lang/String;说的slf4j
Stella981 Stella981
3年前
Maven第四篇【私有仓库、上传jar包、引用私服jar包、上传本地项目到私服】
搭建私有服务器前面已经说过了,我们使用Maven的使用,如果需要导入相对应的jar包,Maven首先会在我们的本地仓库中寻找—私有仓库—中心仓库…然而,我们的本地仓库常常没有想要的jar包的,而经常去中心仓库下载这就非常浪费时间和资源了…因此我们一般都有一个私有仓库…另外有些公司都不提供外网给项目组人员,因此就不能使用mave
Stella981 Stella981
3年前
SpringBoot 引入本地或第三方Jar包
在开发过程中,我们会遇到一些Maven仓库没有的Jar包的情况,比如公司其他团队开发的Jar包等。这时我们就不能通过Pom文件引入。这里我们使用hutoolJar为例。一、使用Maven命令把Jar包添加到本地仓库(1)执行maven命令,把Jar添加到本地。mvninstall:installfileDfile/Us
Wesley13 Wesley13
3年前
Unable to instantiate activity ComponentInfo
不知道怎么回事,在libs中添加了jar包后,无法给jar包附加上源码,于是采取以下措施:删除自动生成的依赖:在AndroidDependences目录上右击BuildPathRemovefromBuildPath然后往libs中拖入jar包,然后添加jar的引用,这样,就可以给jar包附上源码了。但是运行程序的时候
Stella981 Stella981
3年前
Hbase启动hbase shell运行命令报Class path contains multiple SLF4J bindings.错误
1:Hbase启动hbaseshell运行命令报ClasspathcontainsmultipleSLF4Jbindings.错误,是因为jar包冲突了,所以对于和hadoop的jar包冲突的,可以将其他jar包删除,如果你不确定是否删除正确,可以将其他的jar包复制备份或者修改名称,确保操作以后失败了,还可以找回。SLF4J:Cl
kenx kenx
1年前
一键搞定发布自己Jar到Maven中央仓库
做java开发那当然离不开jar包管理,不知何时一直想想封装一个自己的jar包然后发布到maven中央仓库给别人使用。hhh我感觉自己写一个jar包工具然后,被很多人使用是一件很牛,很快乐事情。终于有了这个机会,和时间。SpringBootstater出来
京东云开发者 京东云开发者
2个月前
实际上手体验maven面对冲突Jar包的加载规则
一、问题背景相信大家在日常的开发过程中都遇到过Jar包冲突的问题,emm,在最近处理业务需求时我也遇到了不同版本jar包冲突导致项目加载出错的问题。主要是一个完整的项目会不可避免的使用第三方的Jar包来实现功能开发,各种第三方包之间可能会存在依赖关系,不同