Maven这个词可以翻译为“知识的积累”,也可以翻译为“专家”或“内行”。作为Apache组织中的一个颇为成功的开源项目,Maven主要服务于基于Java平台的项目的构建、依赖管理和项目信息管理。
Maven作为一个构建工具,不仅帮我们自动化构建,还能够抽象构建过程,提供构建任务实现;它跨平台,对外提供了一致的接口操作,这一切足以使它成为优秀的、流行的构建工具。
下载地址:http://maven.apache.org/download.cgi
设置PATH环境变量
export M2_HOME=~/bin/apache-maven-3.6.3
export PATH=$PATH:$M2_HOME/bin
Maven项目的核心是pom.xml
<?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.cage.study.spring</groupId>
<artifactId>spring-study</artifactId>
<version>1.0-SNAPSHOT</version>
<name>Spring Study Project</name>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
以上是一个最基本的包含了主代码和单元测试的pom.xml。
# 编译
mvn clean compile
# 测试
mvn clean test
# 打包
mvn clean package
# 发布到库
mvn clean install
Maven有一些约定:在项目的根目录中放置pom.xml,在src/main/java目录中放置项目的主代码,在src/test/java中放置项目的测试代码。我们将这些基本的目录结构和pom.xml文件内容称为项目的骨架。Maven提供了archetype帮助我们快速勾勒出项目骨架。
mvn archetype:generate
Maven坐标
为了能自动化地解析任何一个Java构件,Maven就必须将它们唯一标识,这就依赖管理的底层基础——坐标。Maven坐标的元素包括:groupId、artifactId、version、packaging、classifier。在我们开发自己项目的时候,也需要为其定义适当的坐标,这是Maven强制要求的。
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
</dependencies>
根元素project下的dependencies可以包含一个或多个dependency元素,以声明一个或者多个项目依赖。每个依赖可以包含的元素有:
- groupId、artifactId和version:依赖的基本坐标。
- type:依赖的类型,默认为jar。
- sope:依赖的范围。
- optional:标记依赖是否可选。
- exclusions:用来排除传递性依赖。
Maven在编译项目主代码的时候需要使用一套classpath;在编译和执行测试的时候会使用另外一套classpath;实际运行Maven项目的时候,又会使用一套classpath。依赖范围是用来控制依赖与这三种classpath(编译classpath、测试classpath、运行classpath)的关系的,Maven有以下几种依赖范围:
- compile:编译依赖范围。如果没有指定,就会默认使用该依赖范围。使用此依赖范围的Maven依赖,对于编译、测试、运行三种classpath都有效。
- test:测试依赖范围。使用此依赖范围的Maven依赖,只对于测试classpath有效,在编译主代码或运行项目的使用时将无法使用此类依赖。
- provided:已提供依赖范围。使用此依赖范围的Maven依赖,对于编译和测试classpath都有效,但在运行时无效。
- runtime:运行时依赖范围。使用此范围的Maven依赖,对于测试和运行classpath有效,但在编译主代码时无效。
- system:系统依赖范围。该依赖与三种classpath的关系,和provided依赖范围完全一致。但是,使用system范围的依赖时必须通过systemPath元素显式地指定依赖文件的路径。由此此类依赖不是通过Maven仓库解析的,而且往往与本机系统绑定,可能造成构建的不可移植,因此应该谨慎使用。systemPath元素可以引用环境变量,如 ${java.home}。
- import:导入依赖范围。该依赖范围不会对三种classpath产生实际影响。
仓库
在Maven世界中,任何一个依赖、插件或项目构建的输出,都可以称为构件。任何一个构件都有一组坐标唯一标识。得益于坐标机制,任何Maven项目使用任何一个构件的方式都是完全相同的。在此基础上,Maven可以在某个位置统一存储所有Maven项目共享的构件,这个统一的位置就是仓库。实际的Maven项目不再各自存储其依赖文件,它们只需要声明这些依赖的坐标,在需要的时候,Maven会自动根据坐标找到仓库中的构件,并使用它们。
任何构件都有其唯一的坐标,根据这个坐标可以定义其在仓库中的唯一存储路径,这便是Maven的仓库布局方式。例如,log4j:log4j:1.2.15这一依赖,其对象的仓库路径为log4j/log4j/1.2.15/log4j-1.2.15.jar。
对于Maven来说,仓库只分为两类:本地仓库和远程仓库。当Maven根据坐标寻找构件的时候,它首先会查看本地仓库,如果本地仓库存在,则直接使用;如果本地仓库没有,或者需要查看是否有更新的构件版本,Maven就会去远程仓库查找,发现需要的构件后下载到本地仓库再使用。
中央仓库是Maven核心自带的远程仓库,它包含了绝大部分开源的构件。在默认配置下,当本地仓库没有Maven需要的构件的时候,它就会尝试从中央仓库下载。私服是一种特殊的远程仓库,为了节省带宽和时间,应该在局域网内架设一个私有的仓库服务器,用其代理所有外部的远程仓库。
Maven默认的本地仓库地址是 ~/.m2/repository,在这里可以看到所有下载的构件。可以通过修改settings配置文件,添加localRepository属性来自定义本地仓库目录。建议将Maven安装目录里conf下的settings.xml拷贝到~/.m2目录下进行维护。
<settings>
<localRepository>/path/to/local/repo</localRepository>
</settings>
Maven的安装文件中自带了中央仓库的地址,所以空配置的情况下依然可以找到中央仓库。以下是从安装目录的lib里maven-model-builder.jar中解压的配置文件,包含了中央仓库的地址。
<repositories>
<repository>
<id>central</id>
<name>Central Repository</name>
<url>https://repo.maven.apache.org/maven2</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>central</id>
<name>Central Repository</name>
<url>https://repo.maven.apache.org/maven2</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases>
<updatePolicy>never</updatePolicy>
</releases>
</pluginRepository>
</pluginRepositories>
生命周期和插件
除了坐标、依赖和仓库外,Maven两外两个核心概念是生命周期和插件。在Maven的日常使用中,生命周期的输入往往就对应了生命周期,如 mvn package 就表示执行默认生命周期阶段 package。Maven的生命周期是抽象的,其实际行为都由插件来完成,如 package 阶段的任务可能就会由 maven-jar-plugin 完成。生命周期和插件协同工作,两者密不可分。
Maven的生命周期就是为了对所有的构建过程进行抽象和统一。Maven从大量项目和构件工具中学习和反思,然后总结了一套高度完善的、易扩展的生命周期。这个生命周期包含了项目的清理、初始化、编译、测试、打包、集成测试、验证、部署和站点生成等几乎所有构建步骤。也就是说,几乎所有项目的构建,都能映射到这样一个生命周期上。
Maven的生命周期是抽象的,这意味着生命周期本身不做任何实际的工作,在Maven的设计中,实际的任务都交由插件来完成。每个构建步骤都可以绑定一个或者多个插件行为,而且Maven为大多数构建步骤编写并绑定了默认插件。
Maven拥有三套相互独立的生命周期,它们分别为clean、default和site。clean的目的是清理项目,default的目的是构建项目,site的目的是建立项目站点。每个生命周期包含一些阶段(phase),这些阶段是有顺序的,并且后面的阶段依赖于前面的阶段。
clean生命周期
- preclean
- clean
- post-clean
default生命周期
- validate
- initialize
- generate-sources
- process-sources 处理项目主资源文件。
- generate-resources
- process-resources
- compile 编译项目的主代码。
- process-classes
- generate-test-sources
- process-test-sources 处理项目测试资源文件。
- generate-test-resources
- process-test-resources
- test-compile 编译项目的测试代码。
- process-test-classes
- test 使用单元测试框架运行测试,测试代码不会被打包或部署。
- prepare-package
- package 将编译好的代码,打包成可发布的格式,如JAR。
- pre-integration-test
- integration-test
- post-integration-test
- verify
- install 将包安装到本地仓库,供其他Maven项目使用。
- deploy 将包安装到远程仓库,供其他开发人员和Maven项目使用。
site生命周期
- pre-site
- site 生成项目站点文档。
- post-site
- site-deploy 将生成的项目站点发布到服务器上。
从命令行执行Maven任务的最主要方式就是调用Maven的生命周期阶段。需要注意的是,各个生命周期是相互对立的,而一个生命周期的阶段是有前后依赖关系的。
一个插件可以完成一个或多个功能,每个功能就是一个插件目标。比如 maven-dependency-plugin 有十多个目标,每个目标对应了一个功能:dependency:analyze、dependency:tree和dependency:list等。这是一种通用的写法,冒号前面是插件前缀,后面是该插件的目标。
Maven的生命周期与插件相互绑定,用以完成实际的构建任务。具体而言,是生命周期的阶段与插件的目标相互绑定,以完成某个具体的构建任务。为了能让用户几乎不用任何配置就能构建Maven项目,Maven在核心为一些主要的生命周期阶段绑定了很多插件的目标,当用户通过命令行调用生命周期阶段的时候,对应的插件目标就会执行相应的任务。
clean生命周期阶段与插件目标的绑定关系
生命周期阶段 | 插件目标 |
---|---|
clean | maven-clean-plugin:clean |
default生命周期阶段与插件目标的绑定关系
生命周期阶段 | 插件目标 |
---|---|
process-resources | maven-resources-plugin:resources |
compile | maven-compiler-plugin:compile |
process-test-resources | maven-resources-plugin:testResources |
test-compile | maven-compiler-pugin:testCompile |
test | maven-surefire-plugin:test |
package | maven-jar-plugin:jar |
install | maven-install-plugin:install |
deploy | maven-deploy-plugin:deploy |
site生命周期阶段与插件目标的绑定关系
生命周期阶段 | 插件目标 |
---|---|
site | maven-site-plugin:site |
site-deploy | maven-site-plugin:deploy |
下面是自定义一个绑定关系,将项目打包成可执行的jar包,需要使用shade插件。
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.2</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.cage.study.App</Main-Class>
</manifestEntries>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
聚合与继承
为了能够使用一条命令就能构建多个模块,我们可以额外添加一个Maven项目,然后使用聚合的POM配置来进行整合。
<groupId>com.cage.study.spring</groupId>
<artifactId>spring-study</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>Wallet</module>
<module>Ticker</module>
</modules>
<name>Spring Study Project</name>
对于聚合项目来说,其打包方式packaging的值必须为pom,否则就无法构建。聚合项目本身不做任何功能实现,往往建在模块的最顶层。
聚合项目也可以建在平行目录,但是module的引用需要使用POM文件的相对地址。
<module>../Wallet</module>
<module>../Ticker</module>
为了解决多个模块重复配置相同属性的问题,我们可以额外添加一个Maven项目,然后让其他模块都继承它,以实现一处配置,多出使用的效果。
模块的平行目录建立一个空项目,配置POM与聚合项目类似。
<groupId>com.cage.study.spring</groupId>
<artifactId>pom-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
其他模块继承此模块的配置时,POM添加如下配置:
<parent>
<artifactId>pom-parent</artifactId>
<groupId>com.cage.study.spring</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../pom-parent/pom.xml</relativePath>
</parent>
parent标签即表示要继承的模块,此时需要添加relativePath来引用到被继承模块的POM配置文件。可以被继承的POM元素如下:
- groupId:项目组ID,项目坐标的核心元素。
- version:项目版本,项目坐标的核心元素。
- description:项目的描述信息。
- organization:项目的组织信息。
- inceptionYear:项目的创始年份。
- url:项目的URL地址。
- developers:项目的开发者信息。
- contributors:项目的贡献者信息。
- distributionManagement:项目的部署配置。
- issueManagement:项目的缺陷跟踪系统信息。
- ciManagement:项目的持续集成系统信息。
- scm:项目的版本控制系统信息。
- mailingLists:项目的邮件列表信息。
- properties:自定义的Maven属性。
- dependencies:项目的依赖配置。
- dependencyManagement:项目的依赖管理配置。
- repositories:项目的仓库配置。
- build:包括项目的源码目录配置、输出目录位置、插件配置、插件管理配置等。
- reporting:包括项目的报告输出目录配置、报告插件配置等。
属性
Maven有六类属性,分别为:
- 内置属性:主要有两个常用的内置属性,${basedir}表示项目根目录,即包含pom.xml文件的目录,${version}表示项目版本。
- POM属性:用户可以使用此类属性引用POM文件中对应元素的值。例如${project.artifactId}就对应了<project><artifactId>元素的值。
- 自定义属性:用户可以在POM的<properties>元素下自定义Maven属性,然后直接通过变量名进行引用。
- Settings属性:与POM属性同理,用户使用以 settings. 开头的属性引用 settings.xml 文件中的XML元素的值,如 ${settings.localRepository} 指向用户本地仓库的地址。
- Java系统属性:所有Java系统属性都可以使用Maven属性引用,如 ${user.home} 指向了用户目录。用户可以使用 mvn help:system 查看所有的Java系统属性。
- 环境变量属性:所有环境变量都可以使用以 env. 开头的Maven属性引用,如 ${env.JAVA_HOME} 指代了JAVA_HOME环境变量的值。用户可以使用 mvn help:system 查看所有的环境变量。
网友评论