MAVEN学习笔记-多模块构建

Posted by AceKei on October 5, 2018

转自 MAVEN

1. Maven多模块项目

以模块的形式来组织项目,可以使得项目的组织结构更加清晰更易维护,每个模块都可以进行高内聚和独立部署。模块之间的依赖关系可以自由进行组合,以提高软件组件的重用,同时各模块之间能够实现松耦合。

水平项目布局

水平项目布局(Flat Project Layout)指的是父模块项目与子模块项目处于同一级目录下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
maven-multiple-modules-project  // 项目根目录
  |
  |— parent-module                  // 父模块项目
  |      |— pom.xml (pom)
  |
  |— child1-module                  // 子模块项目
  |      |— src
  |      |— pom.xml (jar)
  |
  |— child2-module                  // 子模块项目
  |      |— src
  |      |— pom.xml (jar)
  |
  |— child3-module                  // 子模块项目
  |      |— src
  |      |— pom.xml (war)
  |

分层项目布局

分层项目布局(Hierachical Project Layout)指的是父模块项目作为父目录,该目录下包含了所有的子模块项目。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
maven-multiple-modules-project  // 父模块项目
  |
  |— pom.xml (pom)
  |
  |— child1-module                  // 子模块项目
  |      |— src
  |      |— pom.xml (jar)
  |
  |— child2-module                  // 子模块项目
  |      |— src
  |      |— pom.xml (jar)
  |
  |— child3-module                  // 子模块项目
  |      |— src
  |      |— pom.xml (war)
  |

项目布局的差异

水平和分层结构布局的最主要差异在于父模块pom.xml文件是否与子模块处于同一级目录中。
分层项目布局父模块pom.xml文件与子模块处在同一级目录中,而水平项目布局的方式是处在不同级目录。
在多模块项目结构中,当父模块pom.xml文件与子模块处在同一级目录时,它能够给我们带来许多方便之处。
如在父模块POM文件中声明子模块时,分层项目布局的声明方式为:

1
2
3
4
5
<modules>
    <module>child1-module</module>
    <module>child2-module</module>
    <module>child3-module</module>
</modules>

而水平项目布局的声明方式(因其父模块POM文件与子模块处在不同级的目录中):

1
2
3
4
5
<modules>
    <module>../child1-module</module>
    <module>../child2-module</module>
    <module>../child3-module</module>
</modules>

显然第一种方式要比第二种方式更加简洁。总的来说,推荐使用分层项目布局的方式来组织项目的结构。

创建分层结构布局的项目

文档传送门:http://maven.apache.org/plugins/maven-eclipse-plugin/reactor.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
maven-multiple-modules-project  // 父模块项目
  |
  |— pom.xml (pom)
  |
  |— user-facade                    // 子模块项目
  |      |— src
  |      |— pom.xml (jar)
  |
  |— user-service                   // 子模块项目
  |      |— src
  |      |— pom.xml (jar)
  |
  |— blog-web                       // 子模块项目
  |      |— src
  |      |— pom.xml (war)
  |

创建父模块项目maven-multiple-modules-sample

1
mvn archetype:generate -DgroupId=org.fanlychie -DartifactId=maven-multiple-modules-sample -Dversion=0.0.1-SNAPSHOT

进入父模块目录:

1
cd maven-multiple-modules-sample

删除父模块的src目录(父模块是一个没有源代码的项目,因此不需要src目录):

1
rd /s/q src

父模块项目的打包类型必须为pom,修改pom.xml的打包类型如下:

1
<packaging>pom</packaging>

创建子模块项目user-facade

1
mvn archetype:generate -DgroupId=org.fanlychie -DartifactId=user-facade -Dversion=0.0.1-SNAPSHOT

子模块pom.xml文件中会自动生成其父模块信息:

1
2
3
4
5
<parent>
    <groupId>org.fanlychie</groupId>
    <artifactId>maven-multiple-modules-sample</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</parent>

创建子模块项目user-service

1
mvn archetype:generate -DgroupId=org.fanlychie -DartifactId=user-service -Dversion=0.0.1-SNAPSHOT

创建子模块项目blog-web

1
mvn archetype:generate -DgroupId=org.fanlychie -DartifactId=blog-web -Dversion=0.0.1-SNAPSHOT -DarchetypeArtifactId=maven-archetype-webapp

在父模块POM文件中使用<modules>声明其子模块:

1
2
3
4
5
6
7
8
9
10
11
<!-- modules:
     声明子模块。模块间的先后顺序是没有要求的,maven不需要开发者考虑模块间的依赖关系,
     maven在构建项目时,它会对模块自动进行拓扑排序并确保在依赖模块之前构建依赖的模块项目。
     module:
     它的值通常是子模块项目相对于父模块POM文件的相对路径的名称。
-->
<modules>
    <module>user-facade</module>
    <module>user-service</module>
    <module>blog-web</module>
</modules>

将项目导入IntelliJ IDEA(主流的JAVA IDE开发工具)。菜单:File --> Open...选择父模块项目:

项目导入到IntelliJ IDEA后,IntelliJ IDEA能够识别并自动将各个模块转换成maven项目。如图示:

在命令行中执行打包项目的命令,如在cmd中到父模块根目录下执行:

1
$ mvn clean -Dmaven.test.skip package

可以看到,maven在构建多模块项目时,它会根据模块之间的依赖关系,整理出一个新的构建顺序,然后再按这个顺序来构建各个模块。构建完成之后,会在各个子模块项目的target目录下生成输出相对应的jarwar软件包。

IntelliJ IDEA中运行web项目:

创建水平结构布局的项目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
maven-multiple-modules-sample   // 项目根目录
  |
  |— parent-module                  // 父模块项目
  |      |— pom.xml (pom)
  |
  |— user-facade                    // 子模块项目, 用户接口层
  |      |— src
  |      |— pom.xml (jar)
  |
  |— user-service                   // 子模块项目, 用户业务层
  |      |— src
  |      |— pom.xml (jar)
  |
  |— blog-web                       // 子模块项目, 访问控制层
  |      |— src
  |      |— pom.xml (war)
  |

按分层结构布局方式创建多模块项目,然后在项目根目录maven-multiple-modules-sample下创建父模块目录parent-module,并将maven-multiple-modules-sample/pom.xml文件移动到parent-module文件中。修改parent-module/pom.xml<artifactId><name><modules>)配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.fanlychie</groupId>
    <artifactId>parent-module</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>
    <name>parent-module</name>
    <url>http://maven.apache.org</url>
    <!-- 子模块的相对路径(相对父模块pom.xml的路径) -->
    <modules>
        <module>../user-facade</module>
        <module>../user-service</module>
        <module>../blog-web</module>
    </modules>
</project>

子模块项目pom.xml配置文件中继承父模块部分的配置修改为:

1
2
3
4
5
6
7
<parent>
    <groupId>org.fanlychie</groupId>
    <artifactId>parent-module</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <!-- 用于指定父模块POM的相对路径。它的默认值是"../" -->
    <relativePath>../parent-module</relativePath>
</parent>

子模块POM继承父模块POM,在子模块POM配置中<relativePath>用于指定其父模块的POM文件的相对路径。如果<relativePath>配置的值是一个目录,则默认寻找该目录下的/pom.xml文件。即等效于:

1
2
3
4
5
6
7
<parent>
    <groupId>org.fanlychie</groupId>
    <artifactId>parent-module</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <!-- 用于指定父模块POM的相对路径。它的默认值是"../" -->
    <relativePath>../parent-module/pom.xml</relativePath>
</parent>

将项目导入IntelliJ IDEA。菜单:File --> Open...选择项目的根目录:

导入后是一个普通文件目录,IntelliJ IDEA不能够识别出是maven项目,如图示:

接下来需要将项目转换为maven项目,菜单:File --> New --> Module from Existing Sources...

仍然选到项目的根目录:

选择maven,点击下一步:

勾选复选框Search for projects recursively递归搜索项目:

选择maven构建环境:

直接下一步:

选择编译使用的JDK版本,点击完成按钮:

如果项目还报找不到JDK的问题,按快捷键Ctrl+Alt+Shift+S重新选择JDK:

子模块可继承的POM元素

父模块项目POM配置文件中的大部分元素信息能够被子模块所继承,这些元素有:

元素 描述
groupId 项目组ID
version 项目版本号
description 项目描述信息
url 项目的地址
inceptionYear 项目成立年份
organization 项目的组织信息
licenses 项目许可证
developers 项目开发者信息
contributors 项目贡献者信息
mailingLists 项目的邮件列表
scm 项目源代码管理系统信息
issueManagement 项目缺陷跟踪管理系统信息
properties 项目自定义的属性
dependencyManagement 项目的依赖管理配置
dependencies 项目的依赖配置
repositories 项目的仓库配置
pluginRepositories 项目的插件仓库配置
build 项目的构建配置
profiles 项目的环境配置

文档传送门:http://maven.apache.org/pom.html#Inheritance


2. Maven多模块项目版本管理

如果一个项目要迭代一个新的版本,特别是对于一些子模块数量较多的项目,那就需要手工的一个一个的去修改各个模块POM中的版本号信息,这显然不是一个最好的做法。Versions Maven Plugin可用于管理控制Maven多模块项目POM中的版本号信息。

versions:set

用于设置当前项目的版本。如果当前项目是一个父模块项目,则修改会传播给所有的子模块项目。

1
$ mvn versions:set -DnewVersion=0.0.2-SNAPSHOT

在项目pom.xml目录下执行此命令,以更新项目POM的版本号信息。以此同时会在此目录下生成一个pom.xml.versionsBackup文件,该文件是执行更新项目版本号信息前,对当前项目pom.xml文件的一个备份文件。

versions:revert

恢复项目pom.xml文件的上一个版本号信息。回滚的过程依赖生成的pom.xml.versionsBackup文件。如果恢复成功,插件会自动删除pom.xml.versionsBackup文件。

versions:commit

提交项目版本号信息的修改。提交后插件会自动删除pom.xml.versionsBackup文件。


3. Maven多环境构建配置

一个项目从开发到最后发布上线,通常需要在多套不同的环境经受反复的测试和验证,例如开发环境、测试环境、预生产环境、生产环境等。项目部署到不同的环境时,项目的配置通常也是不同的,例如数据库的数据源配置等。maven提供了一套profiles配置,开发者可以在项目POM文件中预先定义好若干个不同环境的profile配置,项目可以根据不同的构建参数来动态选择其中的一个环境设置。这就意味着相同的一套项目代码,可以在构建时根据传入的不同环境参数打出不同环境的软件包来。这也是maven竭力保证的软件可移植性。

多环境配置

项目pom.xml配置示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<!-- 多环境构建配置 -->
<profiles>
    <!-- 开发环境配置 -->
    <profile>
        <!-- 标识符, 可以通过 -P[id] 来激活 --> <!-- -Pdev -->
        <id>dev</id>
        <!-- 激活条件 -->
        <activation>
            <!-- 可以通过 -D[name]=[value] 来激活 --> <!-- -Denv=dev -->
            <property>
                <name>env</name>
                <value>dev</value>
            </property>
            <!-- 默认激活 -->
            <activeByDefault>true</activeByDefault>
        </activation>
        <!-- 自定义的属性, 可以通过 ${label} 来引用 --> <!-- ${jdbcUrl} -->
        <properties>
            <jdbcUrl>jdbc:mysql://localhost:3306/dev</jdbcUrl>
        </properties>
    </profile>
    <!-- 测试环境配置 -->
    <profile>
        <!-- 标识符, 可以通过 -P[id] 来激活 --> <!-- -Ptest -->
        <id>test</id>
        <!-- 激活条件 -->
        <activation>
            <!-- 可以通过 -D[name]=[value] 来激活 --> <!-- -Denv=dev -->
            <property>
                <name>env</name>
                <value>test</value>
            </property>
        </activation>
        <!-- 自定义的属性, 可以通过 ${label} 来引用 --> <!-- ${jdbcUrl} -->
        <properties>
            <jdbcUrl>jdbc:mysql://localhost:3306/test</jdbcUrl>
        </properties>
    </profile>
</profiles>

自定义属性<properties>需要配合<resources>使用才能发挥作用:

1
2
3
4
5
6
7
8
9
10
11
<build>
    <!-- 类路径资源配置, 最终输出到软件包中 -->
    <resources>
        <resource>
            <!-- 资源目录路径, 此路径是相对当前POM文件的位置 -->
            <directory>src/main/resources</directory>
            <!-- filtering=true, 能够代入具体的值替换${label}占位符 -->
            <filtering>true</filtering>
        </resource>
    </resources>
</build>

项目配置文件src/main/resources/environment.properties的内容如下:

1
mysql.jdbc.url = ${jdbcUrl}

打包开发环境的软件包:

1
$ mvn clean -Pdev package

或:

1
$ mvn clean -Denv=dev package

打包测试环境的软件包:

1
$ mvn clean -Ptest package

或:

1
$ mvn clean -Denv=test package

配置的方式

maven支持的profile配置方式主要有以下几种:

  1. 与项目相关的profile配置可以定义在项目pom.xml文件中;
  2. 与用户相关的profile配置可以定义在用户settings.xml%USER_HOME%/.m2/settings.xml)文件中;
  3. 全局的profile配置可以定义在全局settings.xml%MAVEN_HOME%/.m2/settings.xml)文件中;

外部文件(用户或全局settings.xml文件)方式配置的profile是无法移植的。这种方式你只能通过配置<repositories><pluginRepositories><properties>三个标签元素来影响构建的过程,但并不能改变构建最终输出的结果。

内嵌配置(项目pom.xml文件)方式配置的profile是在项目内部定义的,因此它具有良好的可移植性。这种方式你有更多的标签元素可选择,并且能够改变构建最终输出的结果。这些标签元素有:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<profile>
    <!-- 参与构建的模块 -->
    <modules>...</modules>
    <!-- 构件依赖声明 -->
    <dependencies>...</dependencies>
    <!-- 构件依赖管理 -->
    <dependencyManagement>...</dependencyManagement>
    <!-- 构件发布管理 -->
    <distributionManagement>...</distributionManagement>
    <!-- 插件仓库 -->
    <pluginRepositories>...</pluginRepositories>
    <!-- 自定义属性 -->
    <properties>...</properties>
    <!-- 生成站点的报告信息 -->
    <reporting>...</reporting>
    <!-- 构件仓库 -->
    <repositories>...</repositories>
    <!-- 构建配置 -->
    <build>
        <!-- 默认的构建目标, 在命令行中如果直接执行mvn没有带生命周期阶段, 则默认执行此处配置的阶段 -->
        <defaultGoal>...</defaultGoal>
        <!-- 类路径资源 -->
        <resources>...</resources>
        <!-- 单元测试的类路径资源 -->
        <testResources>...</testResources>
        <!-- 打包的名称 -->
        <finalName>...</finalName>
    </build>
</profile>

激活方式

用户或全局settings.xml以及项目pom.xml的配置可以使用<activeByDefault>来激活:

1
2
3
4
5
6
7
<profile>
    <id>dev</id>
    <activation>
        <!-- 默认激活 -->
        <activeByDefault>true</activeByDefault>
    </activation>
</profile>

用户或全局settings.xml的配置还可以使用<activeProfiles>来激活:

1
2
3
4
5
6
7
<profile>
    <id>dev</id>
</profile>
<activeProfiles>
    <!-- 根据ID标识符来激活 -->
    <activeProfile>dev</activeProfile>
</activeProfiles>

命令行可以使用-P[id]-P [id]-P [id1,id2]来激活:

1
2
3
4
5
6
<profile>
    <id>dev</id>
</profile>
<profile>
    <id>test</id>
</profile>
1
2
3
$ mvn clean -Pdev package
$ mvn clean -P dev package
$ mvn clean -P dev,test package

命令行还可以使用-D[name]=[value]的方式来激活:

1
2
3
4
5
6
7
8
<profile>
    <activation>
        <property>
            <name>env</name>
            <value>dev</value>
        </property>
    </activation>
</profile>
1
$ mvn clean -Denv=dev package

查看激活

1
$ mvn help:active-profiles

输出信息示例如下:

1
2
3
4
The following profiles are active:

 - jdk7-development (source: external)
 - dev (source: org.fanlychie:maven-multiple-modules-sample:0.0.2-SNAPSHOT)

文档传送门:http://maven.apache.org/guides/introduction/introduction-to-profiles.html

文档传送门:http://maven.apache.org/pom.html#Profiles