一、简介
1. Maven:
基于 Java 平台的项目管理和整合工具,将项目的开发和管理过程抽象成一个项目对象模型(POM)。开发人员只需要做一些简单的配置,Maven 就可以自动完成项目的编译、测试、打包、发布以及部署等工作。具有跨平台性。
2. 特点
- 设置简单。
- 所有项目的用法一致。
- 可以管理和自动进行更新依赖。
- 庞大且不断增长的资源库。
- 可扩展,使用 Java 或脚本语言可以轻松的编写插件。
- 几乎无需额外配置,即可立即访问新功能。
- 基于模型的构建:Maven 能够将任意数量的项目构建为预定义的输出类型,例如 JAR,WAR。
- 项目信息采取集中式的元数据管理:使用与构建过程相同的元数据,Maven 能够生成一个网站(site)和一个包含完整文档的 PDF。
- 发布管理和发行发布:Maven 可以与源代码控制系统(例如 Git、SVN)集成并管理项目的发布。
- 向后兼容性:您可以轻松地将项目从旧版本的 Maven 移植到更高版本的 Maven 中。
- 并行构建:它能够分析项目依赖关系,并行构建工作,使用此功能,可以将性能提高 20%-50%。
- 更好的错误和完整性报告:Maven 使用了较为完善的错误报告机制,它提供了指向 Maven Wiki 页面的链接,您将在其中获得有关错误的完整描述。
3.默认项目结构
文件 |
目录 |
Java 源代码 |
src/main/java |
资源文件 |
src/main/resources |
测试源代码 |
src/test/java |
测试资源文件 |
src/test/resources |
打包输出文件 |
target |
编译输出文件 |
target/classes |
二、POM
1. POM(Project Object Model,项目对象模型)是 Maven 的基本组件,它是以 xml 文件的形式存放在项目的根目录下,名称为 pom.xml。
2. POM 中定义了项目的基本信息,用于描述项目如何构建、声明项目依赖等等。
3. 当 Maven 执行一个任务时,它会先查找当前项目的 POM 文件,读取所需的配置信息,然后执行任务。在 POM 中可以设置如下配置:
-
项目依赖
-
插件
-
目标
-
构建时的配置文件
-
版本
-
开发者
-
邮件列表
4. pom.xml示例
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>net.biancheng.www</groupId>
<artifactId>maven</artifactId>
<version>0.0.1-SNAPSHOT</version>
</project>
-
在创建 POM 之前,首先要确定工程组(groupId),及其名称(artifactId)和版本,在仓库中这些属性是项目的唯一标识。
-
所有的 Maven 项目都有一个 POM 文件,所有的 POM 文件都必须有 project 元素和 3 个必填字段:groupId、artifactId 以及 version。
节点 |
描述 |
groupId |
项目组 ID,定义当前 Maven 项目隶属的组织或公司,通常是唯一的。它的取值一般是项目所属公司或组织的网址或 URL 的反写,例如 net.biancheng.www。 |
artifactId |
项目 ID,通常是项目的名称。groupId 和 artifactId 一起定义了项目在仓库中的位置。 |
version |
项目版本。 |
3. 所有的 POM 均继承自一个父 POM,这个父 POM 被称为 Super POM,它包含了一些可以被继承的默认设置。
5. Maven坐标
-
为什么引入坐标:在 Maven 中,任何一个依赖、插件或者项目构建的输出,都可以称为构件。在 Maven 世界中存在着众多构件,在引入坐标概念之前,当用户需要使用某个构件时,只能去对应的网站寻找,使得用户将大量的时间浪费在搜索和寻找上,严重地影响了研发效率。为了解决这个问题,于是 Maven 引入了 Maven 坐标的概念。
-
规定:世界上任何一个构件都可以使用 Maven 坐标并作为其唯一标识,该坐标定义了构件在仓库中的唯一存储路径。Maven 坐标包括 groupId、artifactId、version、packaging 等元素,只要用户提供了正确的坐标元素,Maven 就能找到对应的构件。groupId、artifactId 和 version 是必须定义的,packaging 是可选的。
-
groupId: 项目组 ID,定义当前 Maven 项目隶属的组织或公司,通常是唯一的。它的取值一般是项目所属公司或组织的网址或 URL 的反写,例如 net.biancheng.www。
-
artifactId: 项目 ID,通常是项目的名称。
-
version:版本。
-
packaging:项目的打包方式,默认值为 jar
<project>
<groupId>net.biancheng.www</groupId>
<artifactId>helloMaven</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
</project>
6. Maven依赖
-
如果一个 Maven 构建所产生的构件(例如 Jar 文件)被其他项目引用,那么该构件就是其他项目的依赖。
-
Maven 坐标是依赖的前提,所有 Maven 项目必须明确定义自己的坐标
-
当 Maven 项目需要声明某一个依赖时,通常只需要在其 POM 中配置该依赖的坐标信息,Maven 会根据坐标自动将依赖下载到项目中。
<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/maven-v4_0_0.xsd">
...
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
dependencies 元素可以包含一个或者多个 dependency 子元素,用以声明一个或者多个项目依赖,每个依赖都可以包含以下元素:
-
groupId、artifactId 和 version:依赖的基本坐标,对于任何一个依赖来说,基本坐标是最重要的,Maven 根据坐标才能找到需要的依赖。
-
type:依赖的类型,对应于项目坐标定义的 packaging。大部分情况下,该元素不必声明,其默认值是 jar。
-
scope:依赖的范围。
-
optional:标记依赖是否可选。
-
exclusions:用来排除传递性依赖。
大部分依赖声明只包含 groupId、artifactId 和 version 三个元素,至于 scope、optional 以及 exclusions 等元素,了解即可
-
获取依赖坐标:https://mvnrepository.com/ 搜索、复制
-
导入本地jar包:Maven导入本地jar包
某一个项目需要依赖于存储在本地的某个 jar 包,该 jar 包无法从任何仓库中下载的,这种依赖被称为外部依赖或本地依赖。
在pom.xml中添加<dependency>:
<!--外部依赖-->
<dependency>
<groupId>net.biancheng.www</groupId>
<artifactId>helloMaven</artifactId>
<!--依赖范围-->
<scope>system</scope>
<version>1.0-SNAPSHOT</version>
<!--依赖所在位置-->
<systemPath>D:\maven\helloMaven\target\helloMaven-1.0-SNAPSHOT.jar</systemPath>
</dependency>
- scope 表示依赖范围,这里取值必须是 system,即系统。
- systemPath 表示依赖的本地构件的位置。
三、Maven仓库
-
仓库:统一存放依赖和插件的地方。
-
当 Maven 项目需要某些构件时,只要其 POM 文件中声明了这些构件的坐标,Maven 就会根据这些坐标找自动到仓库中找到并使用它们。
-
Maven 仓库可以分为 2 个大类:本地仓库和远程仓库(*仓库、私服、其他公共仓库)
当 Maven 根据坐标寻找构件时,它会首先查看本地仓库,若本地仓库存在此构件,则直接使用;
若本地仓库不存在此构件,Maven 就会优先去远程仓库中的*仓库查找,再查找其他远程仓库(如果设置其他远程仓库),若发现所需的构件后,则下载到本地仓库使用;
如果本地仓库和远程仓库都没有所需的构件,则 Maven 就会停止处理并抛出错误。
远程仓库还可以分为 3 个小类:*仓库、私服、其他公共仓库:
-
*仓库是由 Maven 社区提供的一种特殊的远程仓库,它包含了绝大多数流行的开源构件。在默认情况下,当本地仓库没有 Maven 所需的构件时,会首先尝试从*仓库下载。
-
私服是一种特殊的远程仓库,它通常设立在局域网内,用来代理所有外部的远程仓库。它的好处是可以节省带宽,比外部的远程仓库更加稳定。
-
除了*仓库和私服外,还有很多其他公共仓库,例如 JBoss Maven 库,Java.net Maven 库等等。
1. 本地仓库:
-
本地仓库实际上就是本地计算机上的一个目录(文件夹),它会在第一次执行 Maven 命令时被创建(当 Maven 项目第一次进行构建时,会自动从远程仓库搜索依赖项,并将其下载到本地仓库中)。
-
构件只有储存在本地仓库中,才能被其他的 Maven 项目使用。构件想要进入本地仓库,除了从远程仓库下载到本地仓库外,还可以使用命令 mvn install 将本地项目的输出构件安装到本地仓库中。
2. *仓库:
-
包含绝大多数流行的开源构件,由 Maven 社区管理,不需要配置,需要通过网络才能访问
http://search.maven.org/#browse
3. 远程仓库:
4. 私服:
-
特殊的远程仓库,它是架设在局域网内的仓库服务,用来代理位于外部的远程仓库(*仓库、其他远程公共仓库)。
-
建立私服后,Maven 依赖搜索顺序:本地仓库——>私服(下载到本地仓库)——>外部的远程仓库(下载并缓存到 Maven 私服)——>报错。
-
一些无法从外部仓库下载到的构件,也能从本地上传到私服供其他人使用。
-
私服的优势:节省外网带宽、下载速度更快、便于部署第三方构件、提高项目的稳定性,增强对项目的控制、降低*仓库得负荷压力。
-
私服的搭建:使用Maven仓库管理器(Apache Archiva、JFrog Artifactory、Sonatype Nexus)。
四、Maven生命周期
1. 使用Maven构建项目就是执行lifecycle,执行到指定的phase为止。每个phase会执行自己默认的一个或多个goal。goal是最小任务单元。
2. Maven的生命周期由一系列阶段(phase)构成,Maven 拥有三套标准的生命周期,三套生命周期本身是相互独立:
- default:用于构建项目
- clean:用于清理项目
- site:用于建立项目站点
2.1 内置的生命周期default包含以下phase:
阶段 |
描述 |
validate |
验证项目是否正确以及所有必要信息是否可用。 |
initialize |
初始化构建状态。 |
generate-sources |
生成编译阶段需要的所有源码文件。 |
process-sources |
处理源码文件,例如过滤某些值。 |
generate-resources |
生成项目打包阶段需要的资源文件。 |
process-resources |
处理资源文件,并复制到输出目录,为打包阶段做准备。 |
compile |
编译源代码,并移动到输出目录。 |
process-classes |
处理编译生成的字节码文件 |
generate-test-sources |
生成编译阶段需要的测试源代码。 |
process-test-sources |
处理测试资源,并复制到测试输出目录。 |
test-compile |
编译测试源代码并移动到测试输出目录中。 |
test |
使用适当的单元测试框架(例如 JUnit)运行测试。 |
prepare-package |
在真正打包之前,执行一些必要的操作。 |
package |
获取编译后的代码,并按照可发布的格式进行打包,例如 JAR、WAR 或者 EAR 文件。 |
pre-integration-test |
在集成测试执行之前,执行所需的操作,例如设置环境变量。 |
integration-test |
处理和部署所需的包到集成测试能够运行的环境中。 |
post-integration-test |
在集成测试被执行后执行必要的操作,例如清理环境。 |
verify |
对集成测试的结果进行检查,以保证质量达标。 |
install |
安装打包的项目到本地仓库,以供其他项目使用。 |
deploy |
拷贝最终的包文件到远程仓库中,以共享给其他开发人员和项目。 |
如果我们运行mvn package
,Maven就会执行default
生命周期,它会从开始一直运行到package
这个phase为止
2.2 Maven另一个常用的生命周期是clean
,它会执行3个phase:
- pre-clean 清理前
- clean 清理(注意这个clean不是lifecycle而是phase)
- post-clean 清理后
更复杂的例子是指定多个phase,例如,运行mvn clean package
,Maven先执行clean
生命周期并运行到clean
这个phase,然后执行default
生命周期并运行到package
这个phase。
2.3 site生命周期:目的是建立和部署项目站点,Maven 能够根据 POM 包含的信息,自动生成一个友好的站点,该站点包含一些与该项目相关的文档。
- pre-site
- site
- post-site
- site-deploy
Maven站点(未看):Maven site(站点)
经常用到的phase其实只有几个:
-
clean:清理
-
compile:编译
-
test:运行测试
-
package:打包
3. 执行一个phase又会触发一个或多个goal,goal的命名总是abc:xyz
这种形式
类比一下:
-
lifecycle相当于Java的package,它包含一个或多个phase;
-
phase相当于Java的class,它包含一个或多个goal;
-
goal相当于class的method,它其实才是真正干活的。
五、Maven插件
1. Maven 实际上是一个依赖插件执行的框架,它执行的每个任务实际上都由插件完成的。Maven 的核心发布包中并不包含任何 Maven 插件,它们以独立构件的形式存在, 只有在 Maven 需要使用某个插件时,才会去仓库中下载。
2. Maven 提供了如下 2 种类型的插件。
插件类型 |
描述 |
Build plugins |
在项目构建过程中执行,在 pom.xml 中的 build 元素中配置 |
Reporting plugins |
在网站生成过程中执行,在 pom.xml 中的 reporting 元素中配置 |
3. 插件目标:plugin-goal
为了提高代码的复用性,通常一个 Maven 插件能够实现多个功能,每一个功能都是一个插件目标,即 Maven 插件是插件目标的集合。我们可以把插件理解为一个类,而插件目标是类中的方法,调用插件目标就能实现对应的功能。
插件目标的通用写法:
[插件名]:[插件目标名]
例如,maven-compiler-plugin 插件的 compile 目标的通用写法为:
maven-compiler-plugin:compile
使用 Maven 命令执行插件的目标:
mvn [插件名]:[目标名]
例如,调用 maven-compiler-plugin 插件的 compile 目标
mvn compiler:compile
4. 插件绑定
为了完成某个具体的构建任务,Maven 生命周期的阶段需要和 Maven 插件的目标相互绑定。
例如,代码编译任务对应了default 生命周期的 compile 阶段,而 maven-compiler-plugin 插件的 compile 目标能够完成这个任务,因此将它们进行绑定就能达到代码编译的目的。
当插件目标绑定到生命周期的不同阶段时,其执行顺序由生命周期阶段的先后顺序决定。如果多个目标绑定到同一个生命周期阶段,其执行顺序与插件声明顺序一致,先声明的先执行,后声明的后执行。
-
内置绑定:Maven 默认为一些核心的生命周期阶段绑定了插件目标,当用户调用这些阶段时,对应的插件目标就会自动执行相应的任务。
生命周期 |
阶段 |
插件目标 |
执行的任务 |
clean |
pre-clean |
|
|
clean |
maven-clean-plugin:clean |
清理 Maven 的输出目录 |
post-clean |
|
|
site |
pre-site |
|
|
site |
maven-site-plugin:site |
生成项目站点 |
post-site |
|
|
site-deploy |
maven-site-plugin:deploy |
部署项目站点 |
default |
process-resources |
maven-resources-plugin:resources |
复制资源文件到输出目录 |
compile |
maven-compiler-plugin:compile |
编译代码到输出目录 |
process-test-resources |
maven-resources-plugin:testResources |
复制测试资源文件到测试输出目录 |
test-compile |
maven-compiler-plugin:testCompile |
编译测试代码到测试输出目录 |
test |
maven-surefire-plugin:test |
执行测试用例 |
package |
maven-jar-plugin:jar/maven-jar-plugin:war |
创建项目 jar/war 包 |
install |
maven-install-plugin:install |
将项目输出的包文件安装到本地仓库 |
deploy |
maven-deploy-plugin:deploy |
将项目输出的包文件部署到到远程仓库 |
-
自定义绑定:用户也可以自己选择将某个插件目标绑定到 Maven 生命周期的某个阶段上
<project>
...
<build>
<plugins>
<!-- 绑定插件 maven-antrun-plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.8</version>
<executions>
<execution>
<!--自定义 id -->
<id>www.biancheng.net clean</id>
<!--插件目标绑定的构建阶段 -->
<phase>clean</phase>
<!--插件目标 -->
<goals>
<goal>run</goal>
</goals>
<!--配置 -->
<configuration>
<!-- 执行的任务 -->
<tasks>
<!--自定义文本信息 -->
<echo>清理阶段,编程帮 欢迎您的到来,网址:www.biancheng.net</echo>
</tasks>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>
配置中除了插件的坐标信息之外,还通过 executions 元素定义了一些执行配置。executions 下的每一个 executin 子元素都可以用来配置执行一个任务。
execution 下各个元素含义如下:
-
id:任务的唯一标识。
-
phase:插件目标需要绑定的生命周期阶段。
-
goals:用于指定一组插件目标,其子元素 goal 用于指定一个插件目标。
-
configuration:该任务的配置,其子元素 tasks 用于指定该插件目标执行的任务。
5. 插件与依赖的区别
依赖:运行时开发时都需要,相当于写代码时候用到的包,需要通过这些包里的函数构建自己的代码,在打包时需要把这些依赖也打包进项目里;
插件:在项目开的发时需要,但是在项目运行时不需要,因此在项目开发完成后不需要把插件打包进项目中。插件是一种工具,例如compile插件是用来编译代码的工具,mybatis插件是用来自动生成数据库dao和mapper的工具。而依赖则是项目工程在编译过程中需要依赖的二方及三方包。
把开发项目看作做一道菜,代码就是作为原材料的菜,依赖就是油盐酱醋,而插件就是盘子和铁锅。依赖是整个项目的一部分,而插件不是。
待补充
参考:Maven教程
Maven基础 - 廖雪峰的官方网站