jar包衝突組建設計書

. 背景

實際開發過程中,使用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!");

 }



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章