写点什么

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!");
}
复制代码


发布于: 刚刚阅读数: 3
用户头像

拥抱技术,与开发者携手创造未来! 2018-11-20 加入

我们将持续为人工智能、大数据、云计算、物联网等相关领域的开发者,提供技术干货、行业技术内容、技术落地实践等文章内容。京东云开发者社区官方网站【https://developer.jdcloud.com/】,欢迎大家来玩

评论

发布
暂无评论
jar包冲突组建设计书_京东科技开发者_InfoQ写作社区