如何使用Java从子POM获取父POM并构建完整父POM层级结构
结论
你不能仅通过model.getParent()返回的Parent对象直接访问父POM。MavenXpp3Reader仅负责将POM的XML内容解析为Java对象,本身不实现Maven的父POM定位、仓库解析逻辑,返回的Parent对象只存储了父POM的groupId、artifactId、version三个坐标属性,没有关联父POM的实际文件位置或内容。
实现逻辑
要递归构建完整的父POM层级,只需按照Maven原生的父POM查找规则,手动定位并逐层解析父POM即可,查找优先级和Maven官方规则完全一致:
- 优先读取当前POM中配置的
<relativePath>标签值,未显式配置时默认值为../pom.xml(即当前POM所在目录的上一级目录下的pom.xml),从该相对路径尝试加载父POM - 若相对路径下不存在匹配的POM,根据父POM的GAV坐标,到本地Maven仓库的对应存储路径查找POM文件
- 若本地仓库也不存在对应POM,可引入Maven Resolver(原Eclipse Aether)依赖从配置的远程仓库拉取对应POM,纯本地项目场景下不需要这一步
可直接运行的代码示例
以下代码不需要额外引入Maven核心之外的依赖,即可实现本地场景下的完整父POM层级构建:
import org.apache.maven.model.Model; import org.apache.maven.model.Parent; import org.apache.maven.model.io.xpp3.MavenXpp3Reader; import java.io.File; import java.io.FileReader; import java.util.ArrayList; import java.util.List; public class PomHierarchyBuilder { // 本地Maven仓库根路径,默认取用户目录下的.m2/repository,若自定义了仓库路径可自行修改 private static final String LOCAL_MAVEN_REPO = System.getProperty("user.home") + "/.m2/repository"; public static List<Model> buildFullPomHierarchy(File rootPom) throws Exception { List<Model> hierarchy = new ArrayList<>(); MavenXpp3Reader reader = new MavenXpp3Reader(); File currentPom = rootPom; while (currentPom != null && currentPom.exists()) { Model currentModel = reader.read(new FileReader(currentPom)); hierarchy.add(currentModel); Parent parent = currentModel.getParent(); if (parent == null) { break; // 已解析到顶层POM,无父节点,终止循环 } // 第一步:尝试从relativePath路径查找父POM String relativePath = parent.getRelativePath() != null ? parent.getRelativePath() : "../pom.xml"; File relativeParentFile = new File(currentPom.getParentFile(), relativePath).getCanonicalFile(); if (relativeParentFile.exists() && isPomMatchParent(relativeParentFile, parent, reader)) { currentPom = relativeParentFile; continue; } // 第二步:relativePath未找到匹配POM,到本地Maven仓库查找 File localRepoParent = getLocalRepoPomPath(parent); if (localRepoParent.exists()) { currentPom = localRepoParent; continue; } // 执行到此处说明本地无对应父POM,按需添加远程仓库拉取逻辑即可 currentPom = null; } return hierarchy; } // 校验找到的POM的GAV和parent标签定义的坐标一致,避免relativePath指向错误文件 private static boolean isPomMatchParent(File pomFile, Parent expectedParent, MavenXpp3Reader reader) throws Exception { Model pomModel = reader.read(new FileReader(pomFile)); // 子POM继承父POM的groupId/version时,自身可以不声明,需要从父节点取值 String actualGroupId = pomModel.getGroupId() != null ? pomModel.getGroupId() : pomModel.getParent().getGroupId(); String actualVersion = pomModel.getVersion() != null ? pomModel.getVersion() : pomModel.getParent().getVersion(); return expectedParent.getGroupId().equals(actualGroupId) && expectedParent.getArtifactId().equals(pomModel.getArtifactId()) && expectedParent.getVersion().equals(actualVersion); } // 根据GAV坐标拼接本地仓库中POM的存储路径,路径规则和Maven一致 private static File getLocalRepoPomPath(Parent parent) { String groupPath = parent.getGroupId().replace(".", "/"); String artifactId = parent.getArtifactId(); String version = parent.getVersion(); String pomFileName = artifactId + "-" + version + ".pom"; return new File(LOCAL_MAVEN_REPO, String.join("/", groupPath, artifactId, version, pomFileName)); } }
调用时传入初始子POM的File对象,方法会按从子到父的顺序返回所有层级的POM Model对象。如果需要支持远程仓库拉取,只需要引入maven-resolver相关依赖,在本地仓库查找失败的分支中调用Resolver接口拉取对应GAV的POM到本地后,再进入下一轮解析即可。
内容的提问来源于stack exchange,提问作者Roman Dandali
相关产品推荐
相关产品推荐

