gpt4 book ai didi

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

转载 作者:搜寻专家 更新时间:2023-10-31 19:37:09 26 4
gpt4 key购买 nike

如何在 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。

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

最佳答案

在 Maven 插件之外,操作 Artifact 的方式是通过 Aether。该团队有一个示例项目来获取名为 ResolveTransitiveDependencies 的给定 Artifact 的传递依赖项。 .一旦你设置了 Aether 依赖项(like shown here),你就可以简单地拥有:

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;
}

它将下载 Artifact 并将它们放入 "target/local-repo"

请注意,您可以在系统 session 中使用 DefaultProxySelectorDefaultMirrorSelector 配置代理和镜像。可以读取 Maven 设置文件并使用它来填充 session ,但是事情变得非常丑陋非常快...


当您需要与 Maven 本身紧密耦合,因为您可以访问 POM 进行处理并希望将设置考虑在内时,以编程方式直接调用 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 主目录(即安装目录),但前提是未设置 "maven.home" 系统属性。

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

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

没有办法只输出已解析 Artifact 文件的路径,而且分类器可能不存在的事实使解析有点复杂(我们不能在 : 上拆分为一个限制,因为路径可以包含 :...)。首先,已解决的 Artifact 位于输出文件中的 "The following files have been resolved:" 行下方,然后,由于所需范围仅为 compile runtime,我们可以使用一个简单的正则表达式获取 Artifact 文件的路径,该正则表达式获取 compile:runtime: 之后的所有内容。然后该路径可以直接用作新文件

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

关于java - Maven:以编程方式获取所有依赖项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40813062/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com