Maven 学习笔记(上)
Maven 学习笔记(上)
如有错误,欢迎指出
初识 maven
这篇文章会带你简单认识一下 Maven,并进行一些基本配置,最后使用 VScode 开始一个 Maven 项目。
- make 使用
makefile构建项目; - ant 使用
build.xml构建 Java 项目; - mvn 使用
pom.xml构建基于 Java 的项目。
Ant
Apache Ant is a Java-based build tool. In theory, it is kind of like make, without make's wrinkles.
Apache -- the world's largest open source foundation
Mvn
Apache Maven 服务于项目的整个生命周期(lifecycle)。
最基本的,你可以把 mvn 当作是一个可有效处理依赖的构建工具、一个包管理工具等。
安装
首先,一个配置好的 Java 开发环境是必需的;
而后进入Download,选择一个合适的镜像,下载apache-maven-3.6.3-bin.zip;
最后,解压并设置环境变量即可。
配置文件
参考:
settings.xml 中的 settings 项包含了用于控制 Maven 行为的所有内容,这也包括本地仓库路径、远程仓库服务和认证信息等。
有两个地方可找到配置文件:
${maven.home}/conf/settings.xml${user.home}/.m2/settings.xml
前者是全局配置,其中${maven.home}表示 Maven 安装目录;后者是用户配置,其中${user.home}表示用户主目录,在 Windows 中,即为%userprofile%。
全局配置总是存在,如果用户配置也存在,那么两者的内容将合并,且用户配置的优先级更高。
至于可配置的内容,官方提供了一份完整的参考:
https://maven.apache.org/ref/3.6.3/maven-settings/settings.html
可以使用如下命令查看当前的配置:
mvn help:effective-settings
仓库
仓库是用于存放制品(artifact)和各种依赖的地方,总体上分为两种:local 和 remote,且两种仓库的结构是一样的。
本地仓库
在构建项目时,Maven 会下载需要的依赖到本地仓库中,以便今后多个项目可以共用。默认的仓库路径为
${user.home}/.m2/repository/
对主目录在 C 盘中的 Windows 用户来说,这会急剧缩减 C 盘可用空间。可设定本地仓库的路径
<settings>
...
<localRepository>/path/to/local/repo/</localRepository>
...
</settings>
注意:这里必须使用绝对路径!
远程仓库
一个仓库有它的地址和 id,除了内置的 Maven central 仓库外,还可以在项目内部(pom.xml)定义其它远程仓库。
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd">
...
<profiles>
<profile>
...
<repositories>
<repository>
<id>codehausSnapshots</id>
<name>Codehaus Snapshots</name>
<releases>
<enabled>false</enabled>
<updatePolicy>always</updatePolicy>
<checksumPolicy>warn</checksumPolicy>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>never</updatePolicy>
<checksumPolicy>fail</checksumPolicy>
</snapshots>
<url>http://snapshots.maven.codehaus.org/maven2</url>
<layout>default</layout>
</repository>
</repositories>
...
</profile>
</profiles>
...
</settings>
当构建项目时,如果没有在本地仓库中找到依赖,或者需要更新的依赖,那么就会从远程仓库中下载。
文档Maven Central Repository里有一幅图,说明了各种仓库间的关系及相关的配置位置等信息:
![]()
镜像
可以为远程仓库配置镜像,这样对远程仓库的访问请求,就会被转到对应的镜像中。
<settings>
...
<mirrors>
<mirror>
<id>alimvn</id>
<mirrorOf>central,jcenter</mirrorOf>
<name>Aliyun Maven</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
</mirrors>
...
</settings>
- id:配置文件中,该镜像的唯一标识;
- name:可选的用户友好的镜像仓库名;
- mirrorOf:所镜像的仓库的 id(可使用模式匹配)。
不同于远程仓库,镜像不在项目内部,而是在外部(settings.xml)定义,这样项目使用者就可以根据需要决定是否使用或使用哪个镜像。
属性
就像 shell 中的变量,Maven 中也提供了称为 property 的东西,可使用它做值替换,官方用语为 插值(interpolation)。对于属性 X,使用 ${X} 获取其值。在 Maven 和项目配置文件中,属性的书写有 5 种风格:
env.X- 通过加上前缀
env.,可以获取系统环境变量;
- 通过加上前缀
project.X- 使用点路径,获取 POM 中对应元素的值。比如:
<project><version>1.0</version></project>中的值1.0,可通过${project.version}获取;
- 使用点路径,获取 POM 中对应元素的值。比如:
settings.x- 使用点路径,获取
settings.xml中对应元素的值;
- 使用点路径,获取
- Java 系统属性
- 所有可通过
java.lang.System.getProperties()访问到的属性,都可作为 POM 属性使用;
- 所有可通过
x- 在项
<properties />中设置的值,如:<properties><key>value</key></properties>中的值可通过${key}访问。
- 在项
Profile
为了保证构建行为的可移植性,Maven 将构建配置放在 POM 中,同时避免对文件系统的引用。但完全避免是不可能的,有些插件必须要访问本地文件系统才能使用。
为了解决这一问题,Maven 支持在 POM 中添加被称为 profile 的子集,并可由多种方式触发。
支持的项
What you can specify in a profile? 这取决于你定义 profile 的位置。
可以把 profile 定义在外部文件(如 settings.xml 或 profiles.xml)中,但考虑到构建的可移植性,只允许设置一些不会明显改变构建结果的项,如下:
| Element | Type | Description |
|---|---|---|
activation |
Activation |
触发逻辑 |
properties/key=value* |
Properties |
(Many) Extended configuration specific to this profile goes here. Contents take the form of <property.name>property.value</property.name> |
repositories/repository* |
List<Repository> |
(Many) The lists of the remote repositories. |
pluginRepositories/pluginRepository* |
List<Repository> |
(Many) The lists of the remote repositories for discovering plugins. |
id |
String |
No description. Default value is: default. |
而在 POM 中,还可以配置更多的项。
在一个 Maven 项目目录下,可使用 mvn help:effective-pom 查看当前所有的配置项,使用 mvn help:active-profiles 查看当前激活的 profile。
触发方式
可在命令行中指定:-P
或在 Maven 配置中直接激活,如下:
<settings>
...
<activeProfiles>
<activeProfile>profile-id</activeProfile>
</activeProfiles>
...
</settings>
或者设置触发逻辑,它包含如下元素:
| Element | Type | Description |
|---|---|---|
activeByDefault |
boolean |
(false)是否默认激活 |
jdk |
String |
匹配到指定的 jdk 时触发 |
os |
ActivationOS |
匹配到指定的 OS 环境时触发 |
property |
ActivationProperty |
根据系统属性的定义决定是否触发 |
file |
ActivationFile |
根据指定文件的存在与否触发 |
POM
参考:
项目对象模型(Project Object Model)是 Maven 工作的基本单元,它是一个包含项目信息和构建配置的 XML 文件。
Super POM
所有的 POM 都继承自 Super POM。
一个常见的问题是,不知道一个没有声明远程仓库的项目 POM,是如何知道从哪里下载依赖的,另外对于配置镜像时出现的 central,不知道它所代表的到底是哪个仓库。
事实上,这些信息都来自 Super POM,因为没有在项目 POM 中提供,所以它继承了 Super POM 中的信息。
最小的 POM
一个 POM 至少要包含如下内容:
- 根元素
project modelversion- 必须设为 4.0.0groupid- 项目的组 idartifactid- 该项目的制品的 idversion- 该制品的版本
在 Maven 世界中,使用一个三元组 <groupId>:<artifactId>:<version> 来唯一标识一个制品,这个三元组被称为这个制品的坐标(Coordinate)。
可以创建一个具有最小 POM 的项目,执行
mvn help:effective-pom,以查看 Super POM 的内容。
打包
对于 packaging 项,有效的值有:jar(默认)、pom、maven-plugin、ejb、war、ear、rar 等。目前而言,使用默认值即可。
These types define the goals bound to a set of lifecycle stages.
至于 goal 和 lifecycle 是什么,以及这些 type 起到什么作用,后面再说。
继承
最基本的,在项 parent 中给出父 POM 的坐标,这将在父目录中查找 pom.xml,或者,还是在该项中,使用元素 relativePath 给出父 POM 的相对路径。
子 POM 中可以不再显式提供组 id 和版本信息,这将从父 POM 中继承。
事实上,子 POM 会继承绝大部分元素,除了:
- artifactid
- name
- prerequisites
依赖
项目依赖管理是 Maven 的一个核心特性。
Transitive Dependencies
Maven 使用传递依赖,也就是说,项目中只需要定义直接的依赖就行了。
传递依赖会让项目库迅速变大,为此,Maven 提供了一些额外的特性来解决这一问题:
- Dependency mediation
- Dependency management
- Dependency scope
- Excluded dependencies
- Optinoal dependencies
在项目中,可以使用 mvn dependency:tree 查看依赖。
Dependency Scope
可以为依赖指定作用域,从而限制依赖的传递,并决定何时将该依赖添加到 classpath 中。
- compile
- 默认作用域。编译依赖在项目的整个生命周期内可用,且会被传递;
- provided
- 这些依赖只在编译和测试时被添加到 classpath 中,且不可传递;
- runtime
- 这些依赖会被添加到用于运行时和测试的 classpath 中;
- test
- 这些依赖仅在测试时可用,且不会被传递;
- system
- 与 provided 类似,只不过需要显式提供包含依赖的 JAR 包,因为 Maven 不会在仓库中查找这类依赖;
- import
- 这是一种很特殊的作用域,后面再说。
Dependency Management
使用下面这些项引入依赖,其中只有依赖的坐标是必须的:
<dependencies>
<dependency>
<groupId/>
<artifactId/>
<version/>
<type/>
<classifier/>
<scope/>
<systemPath/>
<exclusions>
<exclusion>
<artifactId/>
<groupId/>
</exclusion>
</exclusions>
<optional/>
</dependency>
</dependencies>
注意,之前提到依赖,均使用了“定义”一词。类似 C 中变量的声明与定义,“声明“一个依赖并不会引入它,只有”定义“才会。不过也要注意,本文中其它部分并没有严格区分二者的含义。
依赖管理不会引入依赖,它只负责声明依赖。将依赖定义放入 dependencyManagement 项中,即变为声明。
在变量定义前加上
extern关键字,即变为变量声明。
而后,若要引入依赖,就不再需要提供版本信息了。
可能遇到这种情况,存在多个项目,它们具有一些相同的依赖,我需要在每个项目的 pom.xml 中定义这些相同的依赖,并且要保证依赖版本一致,很显然这是一项重复的乏味的工作。
可以为这些项目提供一个父 POM,并在其中提供 dependencyManagement 项,于是在子 POM 中,当需要某个依赖时,就不再需要提供版本号了。注意,这时父 POM 的 packaging 值应置为 pom。
第一个项目
使用如下命令创建一个项目:
mvn archetype:generate ^
-DgroupId=com.mycompany.app ^
-DartifactId=my-app ^
-DarchetypeArtifactId=maven-archetype-quickstart ^
-DarchetypeVersion=1.4 ^
-DinteractiveMode=false
如果没有给出坐标,那么 maven 将进行询问。
之后,将看到当前目录下出现了名为 my-app 的项目文件夹,使用 VScode 打开该项目目录,确保已安装了如下扩展:
另外,建议安装 XML,以提供 XML 语言支持;
如果需要进行项目测试,那么建议安装 Java Test Runner,它支持下列框架:
- JUnit 4 (v4.8.0+)
- JUnit 5 (v5.1.0+)
- TestNG (v6.8.0+)
同时记得在 pom.xml 中添加对相应测试框架的依赖。
我可以从哪里得知依赖的地址?
还可以通过 Lombok Annotations Support for VS Code 获取对 Lombok 的支持,这需要为项目添加依赖。
启用 Lombok 后,扩展会在
settings.json中添加:"java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx1G -Xms100m -javaagent:\"c:\\Users\\never\\.vscode\\extensions\\gabrielbb.vscode-lombok-1.0.1\\server\\lombok.jar\"",以支持静态解析。
深入 maven
to write

浙公网安备 33010602011771号