【问题标题】:Handle the dependencies of a Maven Project after the build在构建后处理 Maven 项目的依赖关系
【发布时间】:2021-10-24 23:43:14
【问题描述】:

所以我的问题涉及 Maven 项目中使用的依赖项。我可以使用maven-shade-plugin 构建一个只有一个 pom 的小项目。所以它包含我使用的所有依赖项,也可以构建为可执行的 JAR。但是对于具有以下结构的大型项目会发生什么:

+- myProject 
   +- pom.xml  ... <packaging>pom, aggregator (<modules>) of frontend & backend
   |
   +- frontend
   |  +- pom.xml  ... <parent>myProject, <packaging>pom, aggregator (<modules>) of window & ***
   |  |  
   |  +- window
   |  |  +- pom.xml  ... <parent>frontend
   |  |
   |  +- ***
   |     +- pom.xml  ... <parent>frontend
   |
   +- backend
      +- pom.xml  ... <parent>myProject, <packaging>pom, aggregator (<modules>) of database & ***
      |
      +- database
      |  +- pom.xml  ... <parent>backend
      |
      +- ***
         +- pom.xml  ... <parent>backend

其中windowdatabase 只是frontendbackend 中的示例项目,其中可能包含更多项目。

当我构建这样的东西时,我也可以使用shade 方法,但我有问题。这是否意味着当在父 myProject 中设置了一些依赖项(带有dependencyManagement)时,这些依赖项被frontend 和或backend 中的许多不同项目使用,例如,当构建到一个JAR 分别包含这些依赖项(例如 JUnit,并且都包含它自己)?当某些项目相互依赖时,例如 window 依赖于 database ,因为 databse 是一个依赖项,它被构建到 window 的 JAR 中,然后它们都将分别包含它们自己的依赖项(两者都有JUnit jar)虽然他们可以使用相同的引用而不是拥有自己的引用?我是否混淆了某些东西,或者以这种方式构建它是一个坏主意?

有没有首选或“更好”的方式来构建这样的东西?

【问题讨论】:

  • 通常仅在构建时单元测试需要 JUnit 的 JAR/依赖项,而不是实时运行时。因此它们不在任何 Fat JAR 中,并且在 POM 中使用 &lt;scope&gt;test 声明。

标签: java maven build jar


【解决方案1】:

首先,这种方法称为多模块构建,非常适合此类事情。

这是否意味着在父级 myProject 中有一些依赖项集(带有dependencyManagement)被前端和/或后端内的许多不同项目使用,例如,

dependencyManagement 的含义存在误解。在 dependencyManagement 中定义工件意味着您可以定义 dependency 而无需在您的模块(窗口、数据库)之一中提供版本号。

dependencyManagement 的目的是在一个中心位置“管理”使用的依赖项列表(通常是多模块构建的根目录 (myProject)。

让我们从一个例子开始。您在dependencyManagement 中定义如下工件:

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.apiguardian</groupId>
      <artifactId>apiguardian-api</artifactId>
      <version>1.1.1</version>
    </dependency>
  </dependencies>
</dependencyManagement>

现在您想使用这个工件(即 jar 文件中的类)。这不会向您的项目添加依赖项。对他们都没有。

所以现在要真正使用它,您必须在您的pom.xml 文件之一中定义一个dependency。例如 window 看起来像这样:

<dependencies>
  <dependency>
    <groupId>org.apiguardian</groupId>
    <artifactId>apiguardian-api</artifactId>
  </dependency>
</dependencies>

这将起到真正的作用。这意味着在构建您的模块window 期间,此依赖项(jar 文件)将在类路径中可用,您可以编译使用该依赖项的类的类。您现在可以将此依赖项添加到您的几个模块中(请记住,不要定义版本)。

经过一段时间的开发,您希望升级此工件的版本。这可以通过简单地更改dependencyManagement 位置(仅一个位置)中的版本以及将使用该版本的所有模块轻松实现。

所以第二个误解。由 Maven(以及其他人)构建的 jar 确实包含它的依赖项。

包含依赖项的 jar 称为 fat-jarshaded-jarjar-with-dependenciesexecutable-jar(略有不同)通常不会被创建,也不应该被用作其他人使用的依赖项.

当一些项目相互依赖时,例如 window 依赖于 database ,因为数据库是一个依赖,它被构建到 window 的 JAR 中,然后两者都将分别包含自己的

如前所述,jar 默认情况下不包含依赖项(并且永远不应该)。

如果您在多模块构建的模块之间建立依赖关系,这没问题,从技术上讲,结果是在构建和/或测​​试期间在类路径中找到依赖关系。

所以另一件事是关于测试依赖项,正如您已经提到的。我以 assertj 库为例。如前所述,工件本身将按照您可能已经在dependencyManagement 中猜到的方式定义。这意味着我们将这个工件添加到dependencyManagement。

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.assertj</groupId>
      <artifactId>assertj-core</artifactId>
      <version>3.19.0</version>
    </dependency>
    <dependency>
      <groupId>org.apiguardian</groupId>
      <artifactId>apiguardian-api</artifactId>
      <version>1.1.1</version>
    </dependency>
  </dependencies>
</dependencyManagement>

所以现在您想在其中一个模块中使用assertj 部分,但仅用于测试。我们增强了模块的 dependency 部分......因为我们必须编写一些看起来像这样的测试:

<dependencies>
  <dependency>
    <groupId>org.apiguardian</groupId>
    <artifactId>apiguardian-api</artifactId>
  </dependency>
  <dependency>
    <groupId>org.assertj</groupId>
    <artifactId>assertj-core</artifactId>
    <scope>test</scope>
  </dependency>
</dependencies>

因此,您将在这里看到的最重要的是&lt;scope&gt;test&lt;/scope&gt;。这意味着依赖项仅在单元测试期间“可见”。这意味着默认情况下测试代码位于src/test/java。所以在每个模块中你必须使用 assertj 你必须添加这个依赖项(使用范围测试)。

所以最后你想为你的前端和后端构建一个可执行的 jar。

通常的方法是创建一个单独的模块frontend-appbackend-app,它会生成一个可执行的 jar 文件。首先你必须在frontend-app 中定义使用的模块。这看起来像这样:

<dependencies>
  <dependency>
    <groupId>...</groupId>
    <artifactId>window</artifactId>
    <version>${project.version}</version>
  </dependency>
  <dependency>
    <groupId>...</groupId>
    <artifactId>supplemental modules for frontend</artifactId>
  </dependency>
   ...
</dependencies>

看到给定的版本是由占位符${project.version} 完成的,它会自动替换为项目的定义版本,这一点非常重要。 ${project.version} 的使用表明依赖是由你自己的多模块构建构建的。

这些依赖项对于有正确的模块构建顺序是绝对必要的。通过使用依赖项,Maven 可以自动确定正确的构建顺序。

当然,您必须进行配置以创建可执行 jar。这可以通过 maven-shade-plugin 或 maven-assembly-plugin (基于用例)来完成。如果这是一种 Spring Boot 项目,您应该为此目的使用 spring-boot-maven-plugin。

如果您需要在此模块中编写一些集成测试,您还必须为此添加适当的依赖项。

默认情况下,通常的模块不会生成阴影/胖 jar。通常没有理由这样做。

【讨论】:

  • 非常感谢!你的回答让我明白了一些事情。但因为它似乎总是和我在一起,所以我的问题措辞错误。我事先理解了dependencyManagement 的含义,它只是在我的喋喋不休中纠缠不清。我故意使用fat-Jar,因为即使不好,它也是我知道的唯一答案。我真正想知道的是,如何构建这样一个项目,以便我有一个可执行的 jar 或 bat 或 exe 或其他任何东西来启动程序并将所有内容打包(在周围的文件系统中),所以我可以复制(甚至安装)程序到另一台电脑上并仍然使用它。
  • 我认为并且仍然认为我必须在“包”中获取程序所需的所有依赖项,因为它只是对它们的引用,而另一个引用不存在机器。这可能是更严重的误解,这可能是最简单的部分,而且它甚至可能在 Maven 中自然而然地发生,我只是做错了,因为我从未有过这样的结果。可能我对此很愚蠢,一旦我知道就会感到非常愚蠢,但有时最简单的概念会飞过我的脑海。不过还是谢谢你^^
  • 描述给人的印象是dependencyManagement和dependencies的区别并不清楚。可执行 jar 可以通过 maven-shade-plugin、maven-assembly-plugin 或在 Spring boot 的情况下通过 spring-boot-maven-plugin (或 quarkus 等类似的东西)创建。我还描述了此类模块的位置。 Windows 的 exe 一般不能用 Java 生成。创建安装包通常可以用 maven-assembly-plugin 构建。如果有一种安装程序,比如 install4j...
  • 所有需要的依赖都将被打包到一个所谓的可执行 jar 中(通过 maven-shade-plugin)或使用 maven-assembly-plugin...
  • 啊,谢谢!现在我有了一个想法。我读了一点汇编插件,你是对的,它正是我想要的。我只需要弄清楚如何正确配置它,但首先我必须让我的项目继续进行,然后我可以在进行时弄清楚。非常感谢你,你帮了大忙!^^祝你有美好的一天。
猜你喜欢
  • 2017-09-20
  • 2021-02-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-02
  • 2012-08-20
  • 2011-04-30
  • 1970-01-01
相关资源
最近更新 更多