一、前言

关于pom解析的方式,常见的我认为有两种:
一种是利用dom tree的结构特性,利用dom4j提供的xml解析工具将pom文件读取为dom tree结构,再层层解析出内容。
第二种方式更为简单高效,也是本文将使用的解析方式,即利用maven命令来将pom文件解析为依赖树文件,再直接读取该文件,利用whitesource公司提供的对pom解析的支持,完成整个解析。该方式的优势在于,利用了专门为解析pom文件而生的工具类,能自动识别pom文件中的标签,避免了手动处理xml文件中各标签的麻烦;同时,当待解析的pom存在父pom时,当加载进来的依赖隐式地导入了别的依赖时,不需要我们自己去处理这个复杂的关系,便能读取出所有的依赖。

二、准备工作

为了能在代码中使用maven命令,并解析依赖树文件,我们首先需要导入如下依赖:

<dependency><groupId>org.apache.maven.shared</groupId><artifactId>maven-invoker</artifactId><version>3.0.1</version>
</dependency>
<dependency><groupId>org.whitesource</groupId><artifactId>maven-dependency-tree-parser</artifactId><version>1.0.5</version>
</dependency>

1. pom解析出依赖树文件

在一个maven项目中,执行命令:mvn dependency:tree ,在控制台可以看到打印出的该项目的依赖树结构。那么,为了将依赖树输出到文件中,需要加上参数:-D outputFile=tree.txt,便能将依赖树文件保存到项目的根目录下(与pom.xml)同级。
在java代码中执行该命令的方式如下:

package com.example.demo;import org.apache.maven.shared.invoker.*;
import java.io.File;
import java.util.Collections;public class MavenRun {public static void main(String[] args) {InvocationRequest request = new DefaultInvocationRequest();// 待解析的pom文件路径,不写默认为解析当前项目pom文件request.setPomFile( new File( "./mypom.xml" ) );request.setGoals( Collections.singletonList( "dependency:tree -D outputFile=tree.txt") );Invoker invoker = new DefaultInvoker();// 你的maven安装路径invoker.setMavenHome(new File("xxx/apache-maven-3.5.4"));try {invoker.execute(request);} catch (MavenInvocationException e) {e.printStackTrace();}}
}

执行以上代码,打开解析出的依赖树文件,可看到输出的依赖关系如下:

这样的结构很直观地给我们展示了导入的依赖结构,不仅方便我们直接观看,同时利用whitesource公司提供的工具支持,能够非常方便地解析成java对象。

2. 读取依赖树文件到java对象

利用whitesource公司提供的工具,我们非常简单地就可以将依赖树文件解析得到Node对象。

package com.example.demo;import fr.dutra.tools.maven.deptree.core.InputType;
import fr.dutra.tools.maven.deptree.core.Node;
import fr.dutra.tools.maven.deptree.core.Parser;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.Reader;public class MvnParse {public static void main(String[] args) throws Exception {InputType type = InputType.TEXT;Reader r = new BufferedReader(new InputStreamReader(new FileInputStream("tree.txt"), "UTF-8"));Parser parser = type.newParser();Node tree = parser.parse(r);System.out.println(tree);}
}

设置断点,我们可以看到Node的整个结构。依赖树文件被解析成了树形连接的Node节点,我们递归便能解析出所有的依赖。

有了上面的基础,我们已经知道了如何利用代码实现pom文件的解析,下面我们将实现一个更完整的解析过程。

三. 从Nexus仓库拉取jar文件,读取pom依赖信息

每个公司都有自己的maven私服(nexus仓库),存储了公司项目的所有jar文件。我们要实现的是一个完整的过程:利用一个dependency的坐标,到nexus仓库拉取下来jar文件,并读取出该jar的pom依赖信息。

要解析jar文件,需要传入jar文件的依赖坐标,形如:

<dependency><groupId>com.example</groupId><artifactId>demo</artifactId><version>0.0.1-SNAPSHOT</version>
</dependency>

解析jar文件的步骤主要有如下三步:

  1. 根据jar坐标,解析出编译jar的下载地址。
  2. 下载编译jar,获取到pom文件。
  3. 解析出pom文件中的依赖。

用代码整合这三步如下,下面将进行代码的拆分:

public static List<Dependency> downloadPomFile(String pomXml, String workBasePath) {//获取到编译jar下载地址String classJarFileUrl = getClassJarFileUrl(pomXml);String classFileName = classJarFileUrl.substring(classJarFileUrl.lastIndexOf(APPEND_CHAR) + 1);try {//下载编译jar,存到本地File classFile = downLoadFile(classJarFileUrl, workBasePath, classFileName);if (sourceFile.length() == 0 || classFile.length() == 0) {log.error("pom对应的目标文件为空");throw new RuntimeException("pom对应的目标文件为空");}//获取到pom文件的内容String nexusPomUrl = classJarFileUrl.substring(0, classJarFileUrl.lastIndexOf(".jar")) + ".pom";String pomContent = cn.hutool.core.io.FileUtil.readString(new URL(nexusPomUrl), "UTF-8");String pomFileName = classFileName.substring(0, classFileName.lastIndexOf(".jar"));//解析出pom文件中的依赖return parseAllDependency(pomContent, pomFileName);}
1. 根据jar坐标,解析出编译jar下载地址
//公司的nexus仓库下载snapshot版本jar的根地址
private static final String SNAP_DEPOT = "http://xxx/nexus/content/repositories/snapshots/";
//公司的nexus仓库下载正式jar的根地址
private static final String PUBLIC_DEPOT = "http://xxx/nexus/content/repositories/public/";/*** 获取编译jar地址* @param pomXml* @return*/
public static String getClassJarFileUrl(String pomXml) {Dependency dependency = parseDependencyInfo(pomXml);return getJarFileUrlFromDependency(dependency);
}/*** 将pom信息转换为dependency* <dependency>* <groupId>com.example</groupId>* <artifactId>demo</artifactId>* <version>0.0.1-SNAPSHOT</version>* </dependency>* * @param pomXml* @return*/
public static Dependency parseDependencyInfo(String pomXml) {Document doc = null;try {doc = DocumentHelper.parseText(pomXml);} catch (DocumentException e) {throw new RuntimeException("pom格式有误");}Element rootEle = doc.getRootElement();Element groupEle = rootEle.element(Constants.GROUP_ID);Element artifactEle = rootEle.element(Constants.ARTIFACT_ID);Element versionEle = rootEle.element(Constants.VERSION);if (groupEle == null || artifactEle == null || versionEle == null) {throw new RuntimeException("pom格式有误");}Dependency dependency = new Dependency();dependency.setGroupId(groupEle.getStringValue());dependency.setArtifactId(artifactEle.getStringValue());dependency.setVersion(versionEle.getStringValue());return dependency;
}/*** 将dependency中解析为jar对应的url* @param dependency* @return*/
public static String getJarFileUrlFromDependency(Dependency dependency) {String version = dependency.getVersion();boolean isSnapshot = version.toUpperCase().contains(SNAPSHOT);StringBuilder urlBuilder = new StringBuilder();urlBuilder.append(isSnapshot ? SNAP_DEPOT : PUBLIC_DEPOT);urlBuilder.append(dependency.getGroupId().trim().replace(".", "/")).append("/");urlBuilder.append(dependency.getArtifactId()).append("/");urlBuilder.append(version).append("/");if (isSnapshot) {//找到最新版快照包return getLatestClass(urlBuilder.toString());} else {urlBuilder.append(dependency.getArtifactId()).append("-").append(version).append(JAR_SUFFIX);}return urlBuilder.toString();
}
2. 下载编译jar,获取到pom文件
/*** 从远程url获取文件源并在本地创建文件** @param remoteUrl 远程url地址* @param savePath 本地文件绝对路径* @param fileName 本地文件名* @return*/
public static File downLoadFile(String remoteUrl, String savePath, String fileName) throws IOException {if (StringUtils.isEmpty(remoteUrl)) {throw new RuntimeException("远程url地址为空");}if (StringUtils.isEmpty(savePath) || StringUtils.isEmpty(fileName)) {throw new RuntimeException("文件保存路径或文件名为空");}File file = new File(savePath + fileName);URL url = new URL(remoteUrl);HttpURLConnection conn = (HttpURLConnection) url.openConnection();conn.setRequestMethod("GET");conn.setConnectTimeout(5 * 1000);//设置用户代理(防止屏蔽程序抓取而返回403错误)conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows XP; DigExt)");//通过输入流获取图片数据InputStream inputStream = conn.getInputStream();FileUtils.copyInputStreamToFile(inputStream, file);return file;
}
3. 解析出pom文件中的依赖
//pom下载的本地存储路径
private static final String POM_DOWNLOAD_PATH = "/tmp/pom/";
//本地maven安装地址
private static final String MAVEN_HOME = "xxx/apache-maven-3.5.4"/*** 从pom中解析出所有依赖* @param pomContent pom的内容* @param pomFileName 不带.pom后缀* @return*/
public static List<Dependency> parseAllDependency(String pomContent, String pomFileName, Constants constants) {//在本地创建pom的临时文件FileWriter fileWriter = null;String pomFilePath = POM_DOWNLOAD_PATH + pomFileName + ".pom";File pomFile = new File(pomFilePath);try {//判断路径是否存在,不存在则创建文件路径File file = new File(pomFilePath.substring(0, pomFilePath.lastIndexOf('/')));if (!file.exists()) {file.mkdirs();}fileWriter = new FileWriter(pomFile, false);fileWriter.write(pomContent);} catch (IOException e) {e.printStackTrace();throw new RuntimeException("pom解析异常");} finally {try {if (fileWriter != null) {fileWriter.flush();fileWriter.close();}} catch (IOException e) {e.printStackTrace();}}//将pom文件解析成依赖树,将结果存入txt文件InvocationRequest request = new DefaultInvocationRequest();request.setPomFile(pomFile);String dependencyTreeFilePath = POM_DOWNLOAD_PATH + pomFileName + ".txt";request.setGoals(Collections.singletonList("dependency:tree -D outputFile=" + dependencyTreeFilePath));Invoker invoker = new DefaultInvoker();invoker.setMavenHome(new File(MAVEN_HOME));try {invoker.execute(request);} catch (MavenInvocationException e) {e.printStackTrace();throw new RuntimeException("pom解析异常");}//解析依赖树文件,得到所有的依赖List<Dependency> dependencyList = Lists.newLinkedList();InputType type = InputType.TEXT;try {Reader r = new BufferedReader(new InputStreamReader(new FileInputStream(dependencyTreeFilePath), "UTF-8"));Parser parser = type.newParser();Node root = parser.parse(r);parseTree(root, dependencyList);} catch (IOException | ParseException e) {e.printStackTrace();throw new RuntimeException("pom解析异常");}return dependencyList;
}/**
* 递归解析依赖树文件,得到所有的依赖* @param root* @param dependencyList*/
private static void parseTree(Node root, List<Dependency> dependencyList) {Dependency dependency = new Dependency();dependency.setGroupId(root.getGroupId());dependency.setArtifactId(root.getArtifactId());dependency.setVersion(root.getVersion());dependencyList.add(dependency);LinkedList<Node> childNodes = root.getChildNodes();if (CollectionUtils.isNotEmpty(childNodes)) {childNodes.forEach(child -> parseTree(child, dependencyList));}
}

至此,便完成了从maven私服(nexus)拉取jar文件,解析项目pom依赖信息。

从maven私服(nexus)拉取jar文件,解析项目pom依赖信息相关推荐

  1. maven私服仓库搭建、上传或者拉取jar包

    maven私服仓库搭建 参考:https://www.cnblogs.com/wuwei928/p/10338307.html 一.使用Docker安装Nexus docker pull sonaty ...

  2. 【Maven】高级应用:私服(nexus)搭建及使用、自定义项目骨架(archtype)

    当我们执行 Maven 构建命令时,Maven 开始按照以下顺序查找依赖的库: Maven版本: version(SNAPSHOT):快照版本.随时更新不稳定的,每个版本都只是特定时间点的快照.同时, ...

  3. CentOS 6.5下搭建Maven私服nexus

    下文将介绍基于CentOs 6.5操作系统搭建Maven Nexus3.6.1-02私服以及简单使用,并且会说明如何将jar包手动上传到私服上. Maven Nexus的官方下载路径:http://w ...

  4. springBoot 怎么利用maven 创建可以执行的jar文件?

    欢迎关注博主公众号:[纯洁的明依]文章由陈晓阳原创. 本人微信:chenxiaoyangzxy. 免费提供本人大量学习资料. 1 为了可以使用maven 创建可执行的jar文件 ,首先需要再pom.x ...

  5. Maven私服Nexus安装与使用,springboot源码解析

    * [登录Nexus](about:blank#Nexus_76)* * [默认仓库说明](about:blank#_96)* [仓库类型](about:blank#_105)* [配置阿里云公共仓库 ...

  6. Jenkins CI服务器搭建及Maven私服Nexus

    Jenkins CI服务器搭建及Maven私服Nexus 一:Jenkins持续集成(CI)1 1.1:Jenkins简介及特性1 1.2:Jenkins安装1 1.3:Jenkins配置1 1.4: ...

  7. Maven私服Nexus搭建

    Maven私服Nexus搭建 一.目的 私服是一台独立的服务器,用于解决团队内部的资源共享与资源同步问题 二.搭建流程 2.1 软件安装 2.1 环境准备: Sonatype公司的一款maven私服产 ...

  8. Maven私服Nexus的搭建

    本文主要介绍Maven私服Nexus的搭建,搭建的初衷是因为某个开发室不能保证连接外网(万维网),所以打算搭建一个Maven私服,提前将需要的jar包部署到私服中. 软件版本 操作系统:centOS ...

  9. Maven私服Nexus的搭建及使用

    私服简介 私服是架设在局域网的一种特殊的远程仓库,目的是代理远程仓库及部署第三方构件.有了私服之后,当 Maven 需要下载构件时,直接请求私服,私服上存在则下载到本地仓库:否则,私服请求外部的远程仓 ...

最新文章

  1. Windows Server基础架构云参考架构:硬件之上的设计
  2. C#学习笔记(一)变量 常量 基本数据类型 其它
  3. 网络设备中的集线器(Hub)是什么?—Vecloud微云
  4. java线程学习之notify方法和notifyAll方法
  5. 对CAS机制的理解(二)
  6. make: *** 没有规则可以创建“default”需要的目标“build”
  7. datastore_使用Spring Session和JDBC DataStore进行会话管理
  8. Core Data系列三——基本使用
  9. javaweb基础知识点记录1
  10. centos不同版本修改主机名的正确方法
  11. regsvr32注册Dll文件时出现0x80004005问题的解决方案
  12. word,wps设置页眉和页码
  13. 重装win10专业版系统
  14. Excel VBA打开IE浏览器的网页
  15. python如何手动编写开根号的算法_手动开根号方法
  16. 在Coordinatorlayout中使用RecyclerView导致recyclerview最后一个item显示不全的问题
  17. 团队项目开发流程总结
  18. 操作系统是如何工作的--------Linux 实验二
  19. Java 三种循环的流程图画法总结(for,while,do-while循环)
  20. 傻瓜式操作实现华为手机与其他品牌电脑实现NFC一碰传

热门文章

  1. crypto加密解密
  2. 互联网协议 — DNS 域名系统
  3. 利用python,20行代码即可实现照片墙,还可以生成爱心形状哟
  4. Activiti(二)简单请假流程实现
  5. 小学计算机神奇的因特网教案,小学信息技术第三册全册教案(第一单元 神奇的动画城)...
  6. 如何从零基础入门并精通PS?PS如何快速入门?
  7. 怎么登录163vip邮箱?163vip邮箱登录方式有哪些?
  8. 坐在宝座上圣洁羔羊(儿童诗班)
  9. Google 再被欧盟调查,安卓系统是如何建立垄断的?
  10. 【Python】Python实验:回文是一个正向和逆向都相同的整数,如123454321、463364、9889。编写函数判断一个整数是否是回文数。