当前位置: 首页 > 面试题库 >

Maven:以编程方式获取所有依赖项

巫马英豪
2023-03-14
问题内容

如何以编程方式在Maven执行环境之外获取Maven模块的所有依赖关系?

到目前为止,我有:

通过maven-core:

Path pomPath = ...;
MavenXpp3Reader reader = new MavenXpp3Reader();
try (InputStream is = Files.newInputStream(pomPath)) {
    Model model = reader.read(is);
    this.mavenProject = new MavenProject(model);
}

并通过jcabi-aether:

File localRepo = Paths.get(System.getProperty("user.home"), ".m2").toFile();
new Classpath(mavenProject, localRepo, "runtime")

到目前为止,这通常正确吗?

现在的问题是,我得到了NullPointerException:

Caused by: java.lang.NullPointerException
    at com.jcabi.aether.Aether.mrepos(Aether.java:197)
    at com.jcabi.aether.Aether.<init>(Aether.java:140)
    at com.jcabi.aether.Classpath.<init>(Classpath.java:125)

因为mavenProject.getRemoteProjectRepositories()返回null。

如何在考虑settings.xml文件(镜像,代理,存储库等)的情况下初始化MavenProject以包含配置的远程存储库?


问题答案:

在Maven插件之外,对工件进行操作的方法是通过Aether。该团队有一个示例项目,以获取名为的给定工件的传递依赖ResolveTransitiveDependencies。设置以太依赖项后(如此处所示),您可以简单地进行以下操作:

public static void main(final String[] args) throws Exception {
    DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator();
    RepositorySystem system = newRepositorySystem(locator);
    RepositorySystemSession session = newSession(system);

    RemoteRepository central = new RemoteRepository.Builder("central", "default", "http://repo1.maven.org/maven2/").build();

    Artifact artifact = new DefaultArtifact("group.id:artifact.id:version");

    CollectRequest collectRequest = new CollectRequest(new Dependency(artifact, JavaScopes.COMPILE), Arrays.asList(central));
    DependencyFilter filter = DependencyFilterUtils.classpathFilter(JavaScopes.COMPILE);
    DependencyRequest request = new DependencyRequest(collectRequest, filter);
    DependencyResult result = system.resolveDependencies(session, request);

    for (ArtifactResult artifactResult : result.getArtifactResults()) {
        System.out.println(artifactResult.getArtifact().getFile());
    }
}

private static RepositorySystem newRepositorySystem(DefaultServiceLocator locator) {
    locator.addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class);
    locator.addService(TransporterFactory.class, FileTransporterFactory.class);
    locator.addService(TransporterFactory.class, HttpTransporterFactory.class);
    return locator.getService(RepositorySystem.class);
}

private static RepositorySystemSession newSession(RepositorySystem system) {
    DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
    LocalRepository localRepo = new LocalRepository("target/local-repo");
    session.setLocalRepositoryManager(system.newLocalRepositoryManager(session, localRepo));
    return session;
}

它将下载工件并将其放入"target/local-repo"

请注意,您可以在系统会话上使用DefaultProxySelector和配置代理和镜像DefaultMirrorSelector。可以读取Maven设置文件并使用它来填充会话,但是事情变得非常难看而且非常快…

当您想要访问MOM并希望将设置考虑在内而希望与Maven本身紧密结合时,以编程方式直接调用Maven会容易得多。在这种情况下,您会对给定POM文件的每个依赖项(包括传递性依赖项)的路径感兴趣。对于该dependency:list目标,以设置在一起outputAbsoluteArtifactFilenametrue,会给(几乎)正是这样。

要以编程方式调用Maven,可以使用Invoker API。将依赖项添加到您的项目中:

<dependency>
  <groupId>org.apache.maven.shared</groupId>
  <artifactId>maven-invoker</artifactId>
  <version>2.2</version>
</dependency>

你可以有:

InvocationRequest request = new DefaultInvocationRequest();
request.setPomFile(new File(pomPath));
request.setGoals(Arrays.asList("dependency:list"));
Properties properties = new Properties();
properties.setProperty("outputFile", "dependencies.txt"); // redirect output to a file
properties.setProperty("outputAbsoluteArtifactFilename", "true"); // with paths
properties.setProperty("includeScope", "runtime"); // only runtime (scope compile + runtime)
// if only interested in scope runtime, you may replace with excludeScope = compile
request.setProperties(properties);

Invoker invoker = new DefaultInvoker();
// the Maven home can be omitted if the "maven.home" system property is set
invoker.setMavenHome(new File("/path/to/maven/home"));
invoker.setOutputHandler(null); // not interested in Maven output itself
InvocationResult result = invoker.execute(request);
if (result.getExitCode() != 0) {
    throw new IllegalStateException("Build failed.");
}

Pattern pattern = Pattern.compile("(?:compile|runtime):(.*)");
try (BufferedReader reader = Files.newBufferedReader(Paths.get("dependencies.txt"))) {
    while (!"The following files have been resolved:".equals(reader.readLine()));
    String line;
    while ((line = reader.readLine()) != null && !line.isEmpty()) {
        Matcher matcher = pattern.matcher(line);
        if (matcher.find()) {
            // group 1 contains the path to the file
            System.out.println(matcher.group(1));
        }
    }
}

这将创建一个调用请求,该请求包含:调用目标和系统属性,就像mvn dependency:list -Dprop=value在命令行上启动一样。使用设置的路径将默认为的标准位置"${user.home}/settings.xml",但也可以使用request.setUserSettingsFile(...)和来指定设置的路径request.setGlobalSettingsFile(...)。仅在"maven.home"未设置系统属性的情况下,才需要将调用者设置为Maven主目录(即安装目录)。

调用的结果dependency:list被重定向到文件,该文件随后进行后处理。该目标的输出包括以下格式的依赖项列表(如果没有分类器,则分类器可能不存在):

group.id:artifact.id:type[:classifier]:version:scope:pathname

没有一种方法只能输出已解析工件的文件的路径,并且分类器可能不存在的事实使解析变得有些复杂(:由于路径可能包含:..
,因此我们无法进行限制分割))。首先,已解析的工件"The following files have been resolved:"在输出文件中的行下方,然后,由于所需作用域仅是compileor
runtime,我们可以使用简单的正则表达式获取工件文件的路径,该正则表达式接受compile:或之后的所有内容runtime:。该路径然后可以直接用作new File

如果后处理过程中的箍圈看起来太脆弱,我想您可以创建自己的插件,该插件仅输出已解析工件的文件名。



 类似资料:
  • 我正试图让maven下载所有的依赖项(编译、测试、插件等)。)这样我就可以避免让我们的dockerized构建浪费不必要的时间一遍又一遍地下载它们。 我们已经对maven build进行了dockerized,这样我们就可以从jenkins运行它,而无需在jenkins机器上安装大量构建特定的依赖项(Java、redis、maven依赖项等)。我们的构建依赖于增量docker构建,它只执行实际需要

  • 我想以编程方式查看广告包的所有ProximityUID。一些文章说这在iOS上是不可能的,但Android是可能的。但我不敢相信,因为我发现神奇的应用程序“BLExplr”有这个功能。我需要在我的应用程序中实现该功能。有人知道怎么做吗?或者有好的例子吗?任何帮助都将不胜感激。 (更新日期2014/1/17) 我相信@davidgyoung的答案是正确的。估计信标的近似UID为“B9407F30-F

  • Java和Maven新手。 我试图配置我的应用程序,以便我可以通过cmd线生成一个jar,其中包含我所有的依赖项。 据我所知,我正在正确设置Pom以使用此插件:https://github.com/javafx-maven-plugin/javafx-maven-plugin 以下是我在Pom中的依赖项: 在我的

  • 问题内容: 我需要将我们的maven build java项目发布给远程质量检查团队。为此,我想下载所有依赖项,并发送给他们,这样他们就不需要下载它们了。 当前,所有依赖项都在pom.xml文件中定义,并且我们使用mvn install或mvn包来构建项目。一些项目成员使用uber jars,其他一些项目则使用jars +依赖项来执行。 什么是最简单的方法来预打包相关的jar文件,以使从Inter

  • 我正在尝试在 Docker 容器中构建/部署Spring启动。 如您所见,我用第一个mvn命令缓存了所有的依赖项,这样我的代码应用程序中的每一个变化都不会触发新的大量依赖项下载。它适用于大多数依赖项,但仍有一些需要下载(即使缓存)。这是第二个mvn命令(包)的日志: (顺便说一句,(0 B在0 B / s)有点奇怪...只是一张支票? 如果我基于第一个maven命令之后的步骤启动一个容器(mvn依

  • Maven将使用其最近的WINS策略自动解决依赖关系冲突,在这种情况下,它将在结果: [INFO]-(commons-collections:commons-collections:jar:2.1:compile-因与2.0冲突而省略) 在本例中,如果选择commons-collections:2.1,我将有一个备用依赖关系树,可能包含多个其他依赖关系。 我目前正在做的工作是识别与其他依赖项有冲突