开发者社区 > 博文 > jar包冲突组建设计书
分享
  • 打开微信扫码分享

  • 点击前往QQ分享

  • 点击前往微博分享

  • 点击复制链接

jar包冲突组建设计书

  • yk****
  • 2024-03-29
  • IP归属:北京
  • 100浏览

    . 背景

    实际开发过程中,使用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!");
    
     }


    文章数
    2
    阅读量
    0

    作者其他文章

    01 无用代码扫描组件设计