jar 包冲突组建设计书
- 2024-04-08 北京
本文字数:6071 字
阅读完需:约 20 分钟
. 背景
实际开发过程中,使用 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<String> groupIdsToCheck = null; if (groupId != null) { String[] a = groupId.split(","); if (a.length > 0) { groupIdsToCheck = new HashSet<String>(); for (int i = 0; i < a.length; i++) { groupIdsToCheck.add(a[i].trim()); } } } Set<String> artifactIdsToCheck = null; if (artifactId != null) { String[] a = artifactId.split(","); if (a.length > 0) { artifactIdsToCheck = new HashSet<String>(); 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<ClzWrapper>> data = new HashMap<String, List<ClzWrapper>>(); // get the final artifacts Set<Artifact> artifacts = this.getProject().getArtifacts(); for (Iterator<Artifact> 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<JarEntry> 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<ClzWrapper> clzInfos = new ArrayList<ClzWrapper>(); 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<String> totalConflictJarNum = new HashSet<String>(); int totalConflictClassNum = 0; Set<String> set = data.keySet(); List<String> list = new ArrayList<String>(set); Collections.sort(list, new ClassConflictsComparator()); Iterator<String> iter = list.iterator(); List<JarConflictGroup> jarConflictGroups = new ArrayList<JarConflictGroup>(); Set<String> jarConflictGroupKeys = new HashSet<String>(); // key:jarConflictsGroupKey, value:conflitsClassNum Map<String, Integer> jarConglictGroupConflitsClassNumMap = new HashMap<String, Integer>(); List<ClassConflict> classConflicts = new ArrayList<ClassConflict>(); int classConflictNum = 1; while (iter.hasNext()) { String className = (String) iter.next(); List<ClzWrapper> 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<ClzWrapper> 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<ClzWrapper> 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<JarConflictGroup> iterator = jarConflictGroups.iterator(); iterator.hasNext();) { JarConflictGroup jarConflictGroup = (JarConflictGroup) iterator.next(); jarConflictGroup.setConflitsClassNum(jarConglictGroupConflitsClassNumMap.get(jarConflictGroup.getGroupKey())); int groupTotalClass = 0; List<ArtifactWrapper> artifactWrappers = jarConflictGroup.getArtifactWrappers(); if (artifactWrappers != null && artifactWrappers.size() > 0) { for (Iterator<ArtifactWrapper> 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<JarConflict> jarConflicts = new ArrayList<JarConflict>(); for (Iterator<JarConflictGroup> 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<LogConflict> 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<VersionConflict> 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!");
}
版权声明: 本文为 InfoQ 作者【京东科技开发者】的原创文章。
原文链接:【http://xie.infoq.cn/article/210897411c07a01ac44a7e15f】。文章转载请联系作者。
京东科技开发者
拥抱技术,与开发者携手创造未来! 2018-11-20 加入
我们将持续为人工智能、大数据、云计算、物联网等相关领域的开发者,提供技术干货、行业技术内容、技术落地实践等文章内容。京东云开发者社区官方网站【https://developer.jdcloud.com/】,欢迎大家来玩







评论