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

最后,解压并设置环境变量即可。

配置文件

参考:

https://maven.apache.org/settings.html

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)和各种依赖的地方,总体上分为两种:localremote,且两种仓库的结构是一样的。

本地仓库

在构建项目时,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里有一幅图,说明了各种仓库间的关系及相关的配置位置等信息:

maven-repositories
镜像

可以为远程仓库配置镜像,这样对远程仓库的访问请求,就会被转到对应的镜像中。

<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} 获取;
  • 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.xmlprofiles.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

参考:

POM Reference

项目对象模型(Project Object Model)是 Maven 工作的基本单元,它是一个包含项目信息和构建配置的 XML 文件。

Super POM

所有的 POM 都继承自 Super POM

一个常见的问题是,不知道一个没有声明远程仓库的项目 POM,是如何知道从哪里下载依赖的,另外对于配置镜像时出现的 central,不知道它所代表的到底是哪个仓库。

事实上,这些信息都来自 Super POM,因为没有在项目 POM 中提供,所以它继承了 Super POM 中的信息。

最小的 POM

一个 POM 至少要包含如下内容:

  • 根元素 project
  • modelversion - 必须设为 4.0.0
  • groupid - 项目的组 id
  • artifactid - 该项目的制品的 id
  • version - 该制品的版本

在 Maven 世界中,使用一个三元组 <groupId>:<artifactId>:<version> 来唯一标识一个制品,这个三元组被称为这个制品的坐标(Coordinate

可以创建一个具有最小 POM 的项目,执行 mvn help:effective-pom,以查看 Super POM 的内容。

打包

对于 packaging 项,有效的值有:jar(默认)、pommaven-pluginejbwarearrar 等。目前而言,使用默认值即可。

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

posted @ 2021-03-26 17:32  Char-z  阅读(88)  评论(0编辑  收藏  举报