初识Maven

Maven简介

什么是Maven

Maven项目对象模型(POM),可以通过一小段描述信息来管理项目的构建,报告和文档的软件项目管理工具。

Maven是一个项目管理工具,它包含了一个项目对象模型 (Project Object Model),一组标准集合,一个项目生命周期(Project Lifecycle),一个依赖管理系统(Dependency Management System),和用来运行定义在生命周期阶段(phase)中插件(plugin)目标(goal)的逻辑。

在多个开发团队环境时,Maven可以设置按标准在非常短的时间里完成配置工作。由于大部分项目的设置都很简单,并且可重复使用,Maven让开发人员的工作更轻松,同时创建报表,检查,构建和测试自动化设置。

Maven提供了开发人员的方式来管理:

  • Builds
  • Documentation
  • Reporting
  • Dependencies
  • SCMs
  • Releases
  • Distribution
  • mailing list

概括地说,Maven简化和标准化项目建设过程。处理编译,分配,文档,团队协作和其他任务的无缝连接。 Maven增加可重用性并负责建立相关的任务。

Maven目标

Maven主要目标是提供给开发人员:

  • 项目是可重复使用,易维护,更容易理解的一个综合模型。
  • 插件或交互的工具,这种声明性的模式。

Maven项目的结构和内容在一个XML文件中声明,pom.xml 项目对象模型(POM),这是整个Maven系统的基本单元。

Maven常用概念

  • Maven本地资源库

Maven 的本地资源库是用来存储项目的依赖库,默认的文件夹是 “.m2” 目录,可能需要将其更改为另一个文件夹。

  • Maven中央存储库

Maven 中央存储库是 Maven 用来下载所有项目的依赖库的默认位置。

  • Maven坐标(Coordinate)

Maven坐标定义了一组标识,它们可以用来唯一标识一个项目,一个依赖,或者Maven POM里的一个插件,看一下下面的这个POM文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<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.4.0</modelVersion>

<groupId>mavenbook</groupId>
<artifactId>mybook</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>Maven Quick Start Archetype</name>
<url>http://maven.apache.org</url>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</denpendency>
</denpendencies>
</project>

这个项目的坐标:groupId、artifactId、version 和 packaging。这些组合的标识符拼成了一个项目的坐标,就像任何其它的坐标系统,一个Maven坐标是一个地址,即“空间”里的某个点:从一般到特殊。当一个项目通过依赖,插件或者父项目引用和另外一个项目关联的时候,Maven通过坐标来精确定位一个项目。Maven坐标通常用冒号来作为分隔符来书写,像这样的格式: groupId:artifactId:packaging:version。在上面的pom.xml中,它的坐标可以表示为 mavenbook:my-app:jar:1.0-SNAPSHOT.这个符号也适用于项目依赖,我们的项目依赖JUnit的3.8.1版本,它包含了一个对 junit:junit:jar:3.8.1 的依赖。

  • groupId: 团体、公司、小组、组织、项目或者其它团体。团体标识的约定是:以创建这个项目的组织名称的逆向域名(reverse domain name)开头。来自 Sonatype 的项目有一个以 com.sonatype 开头的 groupId,而 Apache Software 的项目有以 org.apache 开头的groupId。

  • artifactId: 在groupId下的表示一个单独项目的唯一标识符。

  • version: 一个项目的特定版本。发布的项目有一个固定的版本标识来指向该项目的某一个特定的版本。而正在开发中的项目可以用一个特殊的标识,这种标识给版本加上一个“SNAPSHOT”的标记。

    项目的打包格式也是Maven坐标的重要组成部分,但是它不是项目唯一标识符的一个部分。一个项目的 groupId:artifactId:version 使之成为一个独一无二的项目;你不能同时有一个拥有同样的 groupId、artifactId 和 version 标识的项目。

  • packaging: 项目的类型,默认是jar,描述了项目打包后的输出。类型为jar的项目产生一个JAR文件,类型为war的项目产生一个web应用。

在其它“Maven化”项目构成的巨大空间中,这四个元素是定位和使用某个特定项目的关键因素。Maven仓库(repositories)(公共的,私有的,和本地的)是通过这些标识符来组织的。当一个项目被安装到本地的Maven仓库,它立刻能被任何其它的项目所使用。而我们所需要做的只是,在其它项目用使用Maven的唯一坐标来加入对这个特定构件的依赖。

安装配置Maven

无论是Windows还是Ubuntu,安装配置Maven的基本步骤大致相似,一般都按照下载、解压、配置环境变量、验证等几步走。

本文以Ubuntu变例来进行记录。

下载Maven

Apache Maven下载apache-maven-3.5.4-bin.tar.gz:

1
wget http://mirrors.shu.edu.cn/apache/maven/maven-3/3.5.4/binaries/apache-maven-3.5.4-bin.tar.gz

解压下载包到安装目录

解压apache-maven-3.5.4-bin.tar.gz到HOME目录:

1
tar zxvf apache-maven-3.5.4-bin.tar.gz -C ~

配置Maven环境变量

在 /etc/profile 或 ~/.profile 中添加Maven环境变量:

1
2
3
export M2_HOME=/home/username/apache-maven-3.3.91

PATH=$M2_HOME/bin:$PATH

注: Ubuntu中可以直接用命令来完成Maven安装。
$ sudo apt-get install maven

Windows系统的环境变量配置和JDK类似:

验证安装是否成功

1
2
3
4
5
6
7
8
$ mvn -v1

Apache Maven 3.5.2
Maven home: /usr/share/maven
Java version: 1.8.0_144, vendor: Oracle Corporation
Java home: /home/username/Java/jdk1.8/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "4.15.0-36-generic", arch: "amd64", family: "unix"

配置本地仓库

不修改配置的话,下载的文件都会保存到本地仓库 ~/.m2/repository 中。若需指定本地仓库,可修改 $M2_HOME/conf/settings.xml 或将其 copy 到 ~/.m2/settings.xml 再修改 为指定目录。

1
<localRepository>/home/username/.m2/localRepository</localRepository>

配置m2eclipse

在较新的Eclipse中已经集成了m2eclipse,打开 Windows->Preferences,可以看到Maven相关的配置项。

You can install the lastest M2Eclipse release (1.8.3) by using the following update site from within Eclipse:

http://download.eclipse.org/technology/m2e/releases

There are also development builds available. Information on how to install those can be found here.

值得注意的是Embedded内置的默认maven版本是我们即将要修改的,可以通过Add按钮来选择新安装的Maven进行添加。

新建Demo项目

使用mvn命令新建Maven项目

使用 mvn 命令生成Maven项目,需要按照提示选择项目的Archetype并设置正确的Maven坐标(Group Id, Artifact Id, Version)及类包名(Package)。

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
$ mvn archetype:generate

[INFO] Scanning for projects...
......
Choose archetype:
......
Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): 7:
......
Define value for property 'groupId': com.eric.mvndemo
Define value for property 'artifactId': hellomaven
Define value for property 'version' 1.0-SNAPSHOT: :
Define value for property 'package' com.eric.mvndemo: : hellomaven
Confirm properties configuration:
groupId: com.eric.mvndemo
artifactId: hellomaven
version: 1.0-SNAPSHOT
package: hellomaven
Y: :
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Old (1.x) Archetype: maven-archetype-quickstart:1.1
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: basedir, Value: /home/username/Workspace
[INFO] Parameter: package, Value: hellomaven
[INFO] Parameter: groupId, Value: com.eric.mvndemo
[INFO] Parameter: artifactId, Value: hellomaven
[INFO] Parameter: packageName, Value: hellomaven
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] project created from Old (1.x) Archetype in dir: /home/username/Workspace/hellomaven
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 02:16 min
[INFO] Finished at: 2018-09-27T16:37:15+08:00
[INFO] Final Memory: 17M/260M
[INFO] ------------------------------------------------------------------------

使用Eclipse向导新建Maven项目

  1. 选择“Maven Project”
  1. 选择项目名和位置
  1. 选择 Archetype

这里我们创建的是一个简单的Java Demo项目,使用缺省选项即可。

  1. 输入 Archetype 参数

即设置项目的Maven坐标(Group Id, Artifact Id, Version)及类包名(Package)等。

  1. 项目结构

参考:标准Maven项目结构

编写项目代码

  1. 编写主代码
1
2
3
4
5
6
7
8
9
10
11
package com.eric.mvndemo;

public class HelloMaven {
public String sayHello() {
return "Hello, maven!";
}

public static void main(String[] args) {
System.out.println((new HelloMaven()).sayHello());
}
}
  1. 编写junit测试代码
1
2
3
4
5
6
7
8
9
10
11
12
package com.eric.mvndemo;

import static org.junit.Assert.*;
import org.junit.Test;

public class HelloMavenTest {
@Test
public void testSayHello() {
String rst = (new HelloMaven()).sayHello();
assertEquals("Hello, maven!", rst);
}
}

编写 pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?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>com.eric</groupId>
<artifactId>mvndemo</artifactId>
<version>0.0.1-SNAPSHOT</version>

<name>Maven Demo</name>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>

</project>

pom.xml 修改保存后,需要在项目节点 mvndemo 上右击弹出菜单选择执行 Maven->Update Project…以更新项目的Maven Dependencies相关依赖jar包。

编译构建 Maven 项目

Maven 阶段(Maven Phases)及命令目标

一个 Maven 生命周期阶段是在被 Maven 称为“构建生命周期”中的一个步骤。生命周期是包含在一个项目构建中的一系列有序的阶段。Maven 可以支持许多不同的生命周期,但是最常用的生命周期是默认的 Maven 生命周期,这个生命周期中一开始的一个阶段是验证项目的基本完整性,最后的一个阶段是把一个项目发布成产品。

  • validate: 验证项目正确而且所有必要信息可用
  • compile: 编译项目源代码
  • test: 编译执行测试用例
  • package: 编译代码并按分发格式打包,比如 jar
  • integration-test: 集成测试
  • verify: 检查包的有效性及质量标准
  • install: 安装包到本地仓库,以作为其他项目的依赖
  • deploy: 部署发布最终包到远程仓库,以供其他开发者和项目共享

除此之外,还有两个很常用的生命周期阶段。

  • clean: 构建前清理旧的构件
  • site: 生成项目的站点文档

Maven 生命周期阶段(phase)实际上对应映射了 Maven 的插件目标。因此,Maven 命令行并没有指定一个插件目标,而是指定了一个 Maven 生命周期阶段。

mvn <phase> args

例如:

1
mvn clean dependency:copy-dependencies package

这个命令将清理项目、拷贝依赖和打包项目。

注意:可在一条命令中同时指定多个目标。

用 mvn 命令构建

在 pom.xml 所在目录下执行 mvn 命令。

如下命令将完成对项目的清理、测试和打包:

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
~/Workspace/mvndemo$ mvn clean test package

[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Demo 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
......

-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running com.eric.mvndemo.HelloMavenTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.019 sec

Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

......
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.151 s
[INFO] Finished at: 2018-09-27T15:43:46+08:00
[INFO] Final Memory: 21M/193M
[INFO] ------------------------------------------------------------------------

执行成功后,一个新的项目打包文件 mvndemo-0.0.1-SNAPSHOT.jar 将生成在target目录中。

通过 Eclipse 构建

也可以通过 Eclipse 的 “Run As -> Maven build” 来运行 Maven 命令,初次运行需要进行配置。

Maven build 配置

主要是需要对插件目标(Goals)进行设置:

无响应问题处理

在 Eclipse 执行 Maven 会发现没任何响应,控制台也没任何信息输出。这时,需要对 JRE 进行配置,增加一个VM参数:

-Dmaven.multiModuleProjectDirectory=$M2_HOME

最好直接对已安装的 JRE 进行相应设置,这样可以保证 “Run As” 下的所有子菜单都能正确工作。

构建可运行的 jar

一个可运行的jar中,需要在其 META-INF/MANIFEST.MF 中指定正确的 Main-Class。

为了构建这样的jar,我们需要对 pom.xml 进行配置以指导生成正确的 MANIFEST.MF。这需要借助 Maven 的 shade 插件 maven-shade-plugin。

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
<project>
......

<build>
......
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>com.eric.mvndemo.HelloMaven</Main-Class>
<X-Compile-Source-JDK>1.8</X-Compile-Source-JDK>
<X-Compile-Target-JDK>1.8</X-Compile-Target-JDK>
</manifestEntries>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

</project>

如此,通过 mvn package 生成的 jar 将是一个可运行的jar.

1
2
3
~/Workspace/mvndemo$ java -jar ./target/mvndemo-0.0.1-SNAPSHOT.jar

Hello, maven!

参考: Setting Manifest Entries with the ManifestResourceTransformer

安装构件(jar)到本地仓库

Maven 已经提供了一个目标在 maven-install-plugin 中,用于帮助安装一个 JAR 到本地仓库。命令格式为:

mvn install:install-file -Dfile=<path-to-file> -DgroupId=<group-id> -DartifactId=<artifact-id> -Dversion=<version> -Dpackaging=<packaging>

如果已经定义了一个 pom.xm,命令格式写为:

mvn install:install-file -Dfile=<path-to-file> -DpomFile=<path-to-pomfile>

v2.5 之后,maven-install-plugin 已经得到极大优化。如果 JAR 是由 Apache Maven 构建的,它将在其 META-INF 目录中包含一个 pom.xml 文件。这时,命令可以进一步简化为:

mvn install:install-file -Dfile=<path-to-file>

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
~/Workspace/mvndemo$ mvn install:install-file -Dfile=./target/mvndemo-0.0.1-SNAPSHOT.jar -DgroupId=com.eric -DartifactId=mvndemo -Dversion=0.0.1-SNAPSHOT -Dpackaging=jar

[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Demo 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-install-plugin:2.4:install-file (default-cli) @ mvndemo ---
[INFO] Installing /home/username/Workspace/mvndemo/target/mvndemo-0.0.1-SNAPSHOT.jar to /home/username/.m2/repository/com/eric/mvndemo/0.0.1-SNAPSHOT/mvndemo-0.0.1-SNAPSHOT.jar
[INFO] Installing /tmp/mvninstall3743027047168711880.pom to /home/username/.m2/repository/com/eric/mvndemo/0.0.1-SNAPSHOT/mvndemo-0.0.1-SNAPSHOT.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.847 s
[INFO] Finished at: 2018-10-12T15:50:42+08:00
[INFO] Final Memory: 8M/212M
[INFO] ------------------------------------------------------------------------

执行成功后,可在本地仓库中找到该项目包:

参考资源

显示 Gitment 评论