Multi-Module Project with Maven – 使用Maven的多模块项目

最后修改: 2018年 10月 14日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll learn how to build a multi-module project with Maven.

在本教程中,我们将学习如何用Maven构建一个多模块项目。

First, we’ll discuss what a multi-module project is, and have a look at the benefits of following this approach. Then we’ll set up our sample project. For a good introduction to Maven, check out this tutorial.

首先,我们将讨论什么是多模块项目,并看一下采用这种方法的好处。然后,我们将设置我们的示例项目。关于Maven的介绍,请查看本教程

2. Maven’s Multi-Module Project

2.Maven的多模块项目

A multi-module project is built from an aggregator POM that manages a group of submodules. In most cases, the aggregator is located in the project’s root directory and must have packaging of type pom.

一个多模块项目是由一个管理一组子模块的聚合器POM构建的。在大多数情况下,聚合器位于项目的根目录下,并且必须有pom类型的包装。

The submodules are regular Maven projects, and they can be built separately or through the aggregator POM.

子模块是普通的Maven项目,它们可以单独构建,也可以通过聚合器POM构建。

By building the project through the aggregator POM, each project that has a packaging type different from pom will result in a built archive file.

通过聚合器POM构建项目,每个项目的打包类型与pom不同,就会产生一个构建的归档文件。

3. Benefits of Using Multi-Modules

3.使用多模块的好处

The significant advantage of using this approach is that we may reduce duplication.

使用这种方法的显著优势是,我们可能会减少重复工作。

Let’s say we have an application that consists of several modules, a front-end module and a back-end module. Now imagine we work on them and change the functionality, which affects them both. In that case, without a specialized build tool, we’d have to build both components separately or write a script to compile the code, run tests, and show the results. Then, after we got even more modules in the project, it would become harder to manage and maintain.

假设我们有一个由几个模块组成的应用程序,一个前端模块和一个后端模块。现在想象一下,我们对它们进行工作并改变功能,这对它们都有影响。在这种情况下,如果没有专门的构建工具,我们将不得不分别构建这两个组件,或者写一个脚本来编译代码,运行测试,并显示结果。然后,当我们在项目中得到更多的模块后,管理和维护就会变得更加困难。

In the real world, projects may need certain Maven plugins to perform various operations during the build lifecycle, to share dependencies and profiles, and to include other BOM projects.

在现实世界中,项目可能需要某些Maven插件,以便在构建生命周期中执行各种操作,共享依赖关系和配置文件,并包括其他BOM项目

Therefore, when leveraging multi-modules, we can build our application’s modules in a single command, and if the order matters, Maven will figure it out for us. We can also share a vast amount of configuration with other modules.

因此,在利用多模块时,我们可以在一条命令中构建我们应用程序的模块,如果顺序重要,Maven会帮我们弄清楚。我们还可以与其他模块共享大量的配置

4. Parent POM

4.父母POM

Maven supports inheritance in a way that each pom.xml file has the implicit parent POM. It’s called Super POM and can be located in the Maven binaries. These two files are merged by Maven and form the Effective POM.

Maven支持继承的方式是:每个pom.xml文件都有隐含的父POM。它被称为Super POM,可位于Maven二进制文件中。这两个文件被Maven合并,形成有效POM。

We can create our own pom.xml file, which will serve us as the parent project. Then we can include in it all configuration with dependencies, and set it as the parent of our child modules, so they’ll inherit from it.

我们可以创建我们自己的pom.xml文件,它将作为父项目。然后我们可以在其中包含所有的配置和依赖关系,并把它设置为我们的子模块的父项目,这样它们就会从它那里继承。

Besides the inheritance, Maven provides the notion of aggregation. A parent POM that leverages this functionality is called an aggregate POM. Basically, this kind of POM declares its modules explicitly in its pom.xml file.

除了继承,Maven还提供了聚合的概念。利用这一功能的父级POM被称为聚合POM基本上,这种POM在其pom.xml文件中明确声明其模块。

5. Submodules

5.子模块

Submodules, or subprojects, are regular Maven projects that inherit from the parent POM. As we already know, inheritance lets us share the configuration and dependencies with submodules. However, if we’d like to build or release our project in one shot, we have to declare our submodules explicitly in the parent POM. Ultimately, our parent POM will be the parent, as well as the aggregate POM.

子模块,或称子项目,是继承自父POM的普通Maven项目。我们已经知道,继承可以让我们与子模块共享配置和依赖关系。然而,如果我们想一次性构建或发布我们的项目,我们必须在父POM中明确声明我们的子模块。最终,我们的父级POM将是父级,也是聚合POM。

6. Building the Application

6.构建应用程序

Now that we understand Maven’s submodules and hierarchy, let’s build a sample application to demonstrate them. We’ll use Maven’s command-line interface to generate our projects.

现在我们了解了Maven的子模块和层次结构,让我们建立一个示例应用程序来展示它们。我们将使用Maven的命令行界面来生成我们的项目。

This app will consist of three modules that’ll represent:

这个应用程序将由三个模块组成,将代表。

  • The core part of our domain
  • A web service providing some REST APIs
  • webapp containing user-facing web assets of some sort

Since we’ll focus on Maven, the implementation of these services will remain undefined.

由于我们将专注于Maven,这些服务的实现将保持未定义。

6.1. Generating Parent POM

6.1.生成母体POM

First, let’s create a parent project:

首先,让我们创建一个父项目

mvn archetype:generate -DgroupId=com.baeldung -DartifactId=parent-project

Once the parent is generated, we have to open the pom.xml file located in the parent’s directory and add the packaging as pom:

一旦父本生成,我们必须打开位于父本目录下的pom.xml文件,并将包装添加为pom

<packaging>pom</packaging>

By setting the packaging to pom type, we’re declaring that the project will serve as a parent or an aggregator; it won’t produce further artifacts.

通过将包装设置为pom类型,我们宣布该项目将作为一个父级或聚合器;它不会产生进一步的工件。

Now, as our aggregator is done, we can generate our submodules.

现在,由于我们的聚合器已经完成,我们可以生成我们的子模块。

However, we need to note, this is where all the configuration to be shared is located, which will eventually be re-used in child modules. Among other things, we can make use of dependencyManagement or pluginManagement here.

然而,我们需要注意的是,这里是所有需要共享的配置的位置,这些配置最终将在子模块中被重新使用。在其他方面,我们可以在这里使用dependencyManagementpluginManagement

6.2. Creating Submodules

6.2.创建子模块

As our parent POM was named parent-project, we need to make sure we’re in the parent’s directory and run generate commands:

由于我们的父级POM被命名为parent-project,我们需要确保我们在父级的目录中,并运行generate命令。

cd parent-project
mvn archetype:generate -DgroupId=com.baeldung -DartifactId=core
mvn archetype:generate -DgroupId=com.baeldung -DartifactId=service
mvn archetype:generate -DgroupId=com.baeldung -DartifactId=webapp

Notice the command used. It’s the same as we used for the parent. The thing here is, these modules are regular Maven projects, yet Maven recognized that they’re nested. When we changed the directory to the parent-project, it found that the parent has the packaging of type pom, and it will modify the pom.xml files accordingly.

注意所使用的命令。它与我们用于父项目的命令相同。问题是,这些模块是普通的Maven项目,但Maven发现它们是嵌套的。当我们把目录改为parent-project时,它发现父项目有pom,类型的打包,它将相应地修改pom.xml文件。

In the parent-project‘s pom.xml it will add all the submodules inside the modules section:

parent-project的pom.xml中,它将在modules部分中添加所有的子模块。

<modules>
    <module>core</module>
    <module>service</module>
    <module>webapp</module>
</modules>

and in the individual submodules’ pom.xml, it will add the parent-project in the parent section:

而在各个子模块的pom.xml中,它将在parent部分添加父项目。

<parent>
  <artifactId>parent-project</artifactId>
  <groupId>com.baeldung</groupId>
  <version>1.0-SNAPSHOT</version>
</parent>

Next, Maven will generate the three submodules successfully.

接下来,Maven将成功生成三个子模块。

It’s important to note that submodules can have only one parent. However, we can import many BOMs. More details about the BOM files can be found in this article.

值得注意的是,子模块只能有一个父模块。不过,我们可以导入许多BOM。关于BOM文件的更多细节可以在这篇文章中找到。

6.3. Building the Project

6.3.构建项目

Now we can build all three modules at once. In the parent’s project directory, we’ll run:

现在我们可以一次构建所有三个模块。在父方的项目目录中,我们将运行。

mvn package

This will build all the modules. We should see the following output of the command:

这将构建所有的模块。我们应该看到该命令的以下输出。

[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO] parent-project                                                     [pom]
[INFO] core                                                               [jar]
[INFO] service                                                            [jar]
[INFO] webapp                                                             [war]
...
[INFO] Reactor Summary for parent-project 1.0-SNAPSHOT:
[INFO] parent-project ..................................... SUCCESS [  0.272 s]
[INFO] core ............................................... SUCCESS [  2.043 s]
[INFO] service ............................................ SUCCESS [  0.627 s]
[INFO] webapp ............................................. SUCCESS [  0.572 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

The Reactor lists the parent-project, but since it’s pom type it’s excluded, and the build results in three separate .jar files for all the other modules. In this case, build occurs in three of them.

Reactor列出了parent-project,但由于它是pom类型,所以被排除在外,构建的结果是所有其他模块的三个独立的.jar文件。在这种情况下,构建发生在其中的三个。

Moreover, Maven Reactor will analyze our project and build it in the proper order. So if our webapp module depends on the service module, Maven will first build the service, then the webapp.

此外,Maven Reactor会分析我们的项目并按照适当的顺序进行构建。因此,如果我们的webapp模块依赖于service模块,Maven会先构建service,然后再构建webapp

6.3. Enable Dependency Management in Parent Project

6.3.在父项目中启用依赖性管理

Dependency management is a mechanism for centralizing the dependency information for a multi-module parent project and its children.

依赖性管理是一种集中管理多模块父项目及其子项目的依赖性信息的机制。

When you have a set of projects or modules that inherit a common parent, you can put all the required information about the dependencies in the common pom.xml file. This will simplify the references to the artifacts in the child POMs.

当你有一组项目或模块继承了一个共同的父项目,你可以把所有需要的关于依赖关系的信息放在共同的pom.xml文件中。这将简化对子POMs中工件的引用。

Let’s take a look at a sample parent’s pom.xml:

让我们看一下父类的样本pom.xml

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.3.16</version>
        </dependency>
        //...
    </dependencies>
</dependencyManagement>

By declaring the spring-core version in the parent, all submodules that depend on spring-core can declare the dependency using only the groupId and artifactId, and the version will be inherited:

通过在父模块中声明spring-core版本,所有依赖spring-core的子模块可以只使用groupIdartifactId来声明依赖关系,而且版本将被继承。

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
    </dependency>
    //...
</dependencies>

Moreover, you can provide exclusions for dependency management in parent’s pom.xml, so that specific libraries will not be inherited by child modules:

此外,你可以在父模块的pom.xml中为依赖性管理提供排除项,这样特定的库就不会被子模块继承了。

<exclusions>
    <exclusion>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
    </exclusion>
</exclusions>

Finally, if a child module needs to use a different version of a managed dependency, you can override the managed version in the child’s pom.xml file:

最后,如果一个子模块需要使用不同版本的托管依赖,你可以在子模块的pom.xml文件中覆盖托管版本。

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>4.3.30.RELEASE</version>
</dependency>

Please note that while child modules inherit from their parent project, a parent project does not necessarily have any modules that it aggregates. On the other hand, a parent project may also aggregate projects that do not inherit from it.

请注意,虽然子模块继承自其父项目,但父项目不一定有它所聚合的任何模块。另一方面,一个父项目也可以聚合那些不继承于它的项目。

For more information on inheritance and aggregation please refer to this documentation.

关于继承和聚合的更多信息请参考此文档

6.4. Updating the Submodules and Building a Project

6.4.更新子模块和建立项目

We can change the packaging type of each submodule. For example, let’s change the packaging of the webapp module to WAR by updating the pom.xml file:

我们可以改变每个子模块的packaging类型。例如,让我们通过更新pom.xml文件,将webapp模块的packaging改为WAR

<packaging>war</packaging>

and adding maven-war-plugin in the plugins list:

并在插件列表中添加maven-war-plugin

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
            <version>3.3.2</version>
            <configuration>
               <failOnMissingWebXml>false</failOnMissingWebXml>
            </configuration>
        </plugin>
    </plugins>
</build>

Now we can test the build of our project by using the mvn clean install command. The output of the Maven logs should be similar to this:

现在我们可以使用mvn clean install命令来测试我们项目的构建。Maven日志的输出应该与此类似。

[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO] 
[INFO] parent-project                                                     [pom]
[INFO] core                                                               [jar]
[INFO] service                                                            [jar]
[INFO] webapp                                                             [war]
//............. 
[INFO] Reactor Summary for parent-project 1.0-SNAPSHOT:
[INFO] 
[INFO] parent-project ..................................... SUCCESS [  0.272 s]
[INFO] core ............................................... SUCCESS [  2.043 s]
[INFO] service ............................................ SUCCESS [  0.627 s]
[INFO] webapp ............................................. SUCCESS [  1.047 s]

7. Conclusion

7.结语

In this article, we discussed the benefits of using Maven multi-modules. We also distinguished between regular Maven’s parent POM and an aggregate POM. Finally, we explored how to set up a simple multi-module to start to play with.

在这篇文章中,我们讨论了使用Maven多模块的好处。我们还区分了常规Maven的父POM和聚合POM。最后,我们探讨了如何设置一个简单的多模块来开始使用。

Maven is a great tool, but it’s complex on its own. If we want to learn more details about Maven, we can look at the Sonatype Maven reference or Apache Maven guides. If we seek advanced usages of Maven’s multi-modules set-up, we can look at how the Spring Boot project leverages its usage.

Maven是一个伟大的工具,但它本身也很复杂。如果我们想了解关于Maven的更多细节,我们可以看看Sonatype Maven参考Apache Maven指南。如果我们想了解Maven的多模块设置的高级用法,我们可以看看Spring Boot项目如何利用其用法

All the code examples used in this article are available over Github.

本文中使用的所有代码示例都是通过Github提供的。