构建一个项目通常由多个任务组成, 如下载依赖,放入classpath下,编译源码,运行测试,打包,部署等. 而maven则是一个自动化这些任务的工具.
Maven核心上是一个执行插件的框架,所有的工作都由插件完成。插件提供了很多goal,goal可以挂在在构建的不同生命周期中运行. 在super pom(见2.2.2小节)中提供了默认插件, 以提供maven基本功能.
生命周期是项目构建的主要方向, Maven有三个内置的生命周期:
一个生命周期由一系列phase(阶段)组成,当maven执行某个phase时,如mvn package
, 会按顺序执行之前的phase和该phase。实际上phase会被映射到底层的goals,也就是说,真正执行的是goals,并且一个phase可以执行多个goals。根据项目的不同打包类型,每个phase使用的goals可能不同。为了执行内置的生命周期,一些插件会默认被maven使用。
具体关于Lifecycle vs. Phase vs. Plugin vs. Goal的区别,请看参考链接
为了方便生成项目, maven提供了一堆模板供使用, 如mvn archetype:generate ...(一堆参数)
,archetype
是插件提供给这个goal的前缀.
maven有两种类型仓库: 依赖仓库和插件仓库. 从来源也可以将仓库分为本地和远程仓库, 当构建项目时, maven会先从本地查找依赖, 无则从远程仓库查找依赖.
maven项目有个固定的标准的目录结构, 也是super pom中设置好的.
maven项目的配置由pom文件提供, pom文件中所有元素清单如下:
<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.0modelVersion>
<groupId>...groupId>
<artifactId>...artifactId>
<version>...version>
<packaging>...packaging>
<dependencies>...dependencies>
<parent>...parent>
<dependencyManagement>...dependencyManagement>
<modules>...modules>
<properties>...properties>
<build>...build>
<reporting>...reporting>
<name>...name>
<description>...description>
<url>...url>
<inceptionYear>...inceptionYear>
<licenses>...licenses>
<organization>...organization>
<developers>...developers>
<contributors>...contributors>
<issueManagement>...issueManagement>
<ciManagement>...ciManagement>
<mailingLists>...mailingLists>
<scm>...scm>
<prerequisites>...prerequisites>
<repositories>...repositories>
<pluginRepositories>...pluginRepositories>
<distributionManagement>...distributionManagement>
<profiles>...profiles>
project>
用于区分项目
com.baidu
my-project
2.0
,2.0.1
,1.3.1
,而SNAPSHOT
表示正在开发.这些参数仅用于标识项目, 不会对项目结构造成影响, 如package结构. 但会影响maven仓库中项目的存储结构, 如$M2_REPO/com/baidu/my-project/1.0
定义项目打包时生成文件的类型, 默认jar
, 所有可选值: pom
, jar
, maven-plugin
, ejb
, war
, ear
, rar
. packaging
的选择会影响整个生命周期中执行的goals
打包时, 默认会将依赖加入目标包中
项目之间有依赖,继承,多模块关系. 如一个项目依赖另一个项目; 子项目继承父项目, 父项目多用于为子项目管理依赖; 多模块项目的顶层项目主要用于将多个模块(项目)分成一组, 只需对顶层项目构建, 所有模块都会被构建. 一般复杂项目中会同时存在这三种关系.
一个简单的例子:
<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">
...
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<type>jartype>
<scope>testscope>
<optional>trueoptional>
dependency>
...
dependencies>
...
project>
groupId, artifactId, version: 三个坐标确定一个依赖.
type: 依赖类型, 默认jar
scope: 指定依赖什么时候在classpath中可见, 不同类型的scope还会影响它的传递性.
scope | 编译时 | 测试时 | 运行时 | 传递性 | 注意 |
---|---|---|---|---|---|
compile(default) | 提供 | 提供 | 提供 | 有 | |
provided | 提供 | 提供 | 否 | 无 | 一般JDK或容器已提供 如 servlet-api |
runtime | 否 | 提供 | 提供 | 应该有 | 编译时不需要, 用于辅助 如 spring-boot-devtools |
test | 否 | 提供 | 否 | 无 | 用于测试代码 如 junit |
system | 提供 | 提供 | 否 | 无 | 类似provided , 但Jar在系统其他地方提供, 需要配合 systemPath 使用 |
optional: 直接影响依赖的传递性, true
时无传递性, 默认false
systemPath: 仅当scope
为system
时才生效, 指定该依赖的位置.
1.0
: “Soft” requirement on 1.0 (just a recommendation, if it matches all other ranges for the dependency)[1.0]
: “Hard” requirement on 1.0(,1.0]
: x <= 1.0[1.2,1.3]
: 1.2 <= x <= 1.3[1.0,2.0)
: 1.0 <= x < 2.0[1.5,)
: x >= 1.5(,1.0],[1.2,)
: x <= 1.0 or x >= 1.2; multiple sets are comma-separated(,1.1),(1.1,)
: this excludes 1.1 (for example if it is known not to work in combination with this library)还有什么版本顺序, 不知道啥子用, 见Version Order
当传递依赖冲突时, 可以使用exclusion
元素, 排除某个依赖的传递依赖
<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">
...
<dependencies>
<dependency>
<groupId>org.apache.mavengroupId>
<artifactId>maven-embedderartifactId>
<version>2.0version>
<exclusions>
<exclusion>
<groupId>org.apache.mavengroupId>
<artifactId>maven-coreartifactId>
exclusion>
exclusions>
dependency>
...
dependencies>
...
project>
或者使用*
排除某个依赖的所有依赖
<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">
...
<dependencies>
<dependency>
<groupId>org.apache.mavengroupId>
<artifactId>maven-embedderartifactId>
<version>3.1.0version>
<exclusions>
<exclusion>
<groupId>*groupId>
<artifactId>*artifactId>
exclusion>
exclusions>
dependency>
...
dependencies>
...
project>
pom之间是可以继承的, 子pom可继承的元素有:
不能继承的有:
比如, 子pom可以从父pom中继承依赖(dependencies
).
每个pom都有父pom, 没有声明时会隐式继承Super POM, 从中可以看到pom的一些默认设置, 如默认的标准项目结构, 默认运行所必须的插件等.
使用步骤如下:
父pom声明packaging
元素为pom
<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.0modelVersion>
<groupId>org.codehaus.mojogroupId>
<artifactId>my-parentartifactId>
<version>2.0version>
<packaging>pompackaging>
project>
子pom中声明父pom
<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.0modelVersion>
<parent>
<groupId>org.codehaus.mojogroupId>
<artifactId>my-parentartifactId>
<version>2.0version>
<relativePath>../my-parentrelativePath>
parent>
<artifactId>my-projectartifactId>
project>
relativePath
指定父pom位置, 默认../pom.xml
子pom查询父pom的顺序: relativePath
–>本地仓库–>远程仓库
通过该元素可以管理所有子pom的依赖信息. 它和dependencies
不同, 子pom会从父pom中继承dependencies
声明的依赖, 而至于dependencyManagement
, 则子pom中存在dependencyManagement
的依赖时, 会从该元素中获取信息, 如版本号(version
)和作用于(scope
), 因此子pom中可以省略这些信息. 在spring boot项目中该用法十分常见.
多模块项目(也叫Aggregation项目), 由多个模块组成, 每个模块都是一个完整的项目. 这样的好处是将多个项目当作一个组, 执行maven生命周期时, 如打包, 所有的项目都会被打包 , 并且maven会处理好不同项目之间的依赖关系而选择正确的打包顺序, 如先打包dao项目, 后service项目等等.
使用: 首先多模块项目的packaging
声明为pom
, 然后添加modules
元素, 如下所示:
<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.0modelVersion>
<groupId>org.codehaus.mojogroupId>
<artifactId>my-parentartifactId>
<version>2.0version>
<packaging>pompackaging>
<modules>
<module>my-projectmodule>
<module>another-projectmodule>
<module>third-project/pom-example.xmlmodule>
modules>
project>
module
元素中填项目的相对路径或这些项目的pom文件地址
关于继承和多模块
通常继承和多模块会同时存在于一个顶层项目中, 但是他们没有必然关系, 即被继承的父pom项目可以不存在多模块, 多模块项目可以不被子模块继承.
属性是一个占位符, 可以在pom中其他地方使用, 如:
<project>
...
<properties>
<maven.compiler.source>1.7maven.compiler.source>
<maven.compiler.target>1.7maven.compiler.target>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
properties>
...
project>
然后可以通过${x}
来使用它, 如${maven.compiler.source}
三个生命周期所有的phase如下所示:
default lifecycle常用的phases:
所有常用的phases如下(来自idea截图):
该小节冗余, 将废弃, 但是不忍心删除这些内容
一个项目可以由多个相对独立的模块(小项目)组成,每个模块可以存在一个pom文件。为了防止模块之间冗余,父pom抽离子pom间的公共部分,由子pom继承父pom配置,使项目更容易维护。
当父、子pom中属性或依赖冲突时,子pom优先级高。
父pom:通过在pom中定义
来声明;
子pom:pom中添加parent
元素,如:
<parent>
<groupId>top.sidian123.demogroupId>
<artifactId>MavenExamplesartifactId>
<version>0.0.1-SNAPSHOTversion>
parent>
parent
元素中可定义relativePath
元素,表明父pom相对于当前子pom的位置。默认../pom.xml
,如果不填,则从仓库中查找。
参考:Maven – Parent and Child POM Example
dependencyManagement
用在父pom文件中,只有子pom中存在dependencyManagement
中的依赖,才会从该依赖中继承未指定的配置,如spring boot中的版本号。
maven默认资源插件负责将项目中资源拷贝到输出目录中。默认资源放在src/main/resources
中。
更改默认资源文件夹:
<build>
...
<resources>
<resource>
<directory>src/my-resourcesdirectory>
resource>
resources>
...
build>
多个资源目录
<build>
...
<resources>
<resource>
<directory>resource1directory>
resource>
<resource>
<directory>resource2directory>
resource>
<resource>
<directory>resource3directory>
resource>
resources>
...
build>
仅引入目录中匹配成功的资源:
<build>
...
<resources>
<resource>
<directory>[your directory]directory>
<includes>
<include>**/*.txtinclude>
<include>**/*.rtfinclude>
includes>
resource>
...
resources>
...
build>
引入目录中所有资源,除了匹配成功的:
<build>
...
<resources>
<resource>
<directory>src/my-resourcesdirectory>
<excludes>
<exclude>**/*.bmpexclude>
<exclude>**/*.jpgexclude>
<exclude>**/*.jpegexclude>
<exclude>**/*.gifexclude>
excludes>
resource>
...
resources>
...
build>
对
引入的资源用
筛选:
<build>
...
<resources>
<resource>
<directory>src/my-resourcesdirectory>
<includes>
<include>**/*.txtinclude>
includes>
<excludes>
<exclude>**/*test*.*exclude>
excludes>
resource>
...
resources>
...
build>
参考:Apache Maven Resources Plugin
总的,插件可以被归为两类:
元素中配置
元素中配置首先安装了JDK, 并且设置环境变量JAVA_HOME
export JAVA_HOME=/home/sidian/Software/jdk-11.0.3
官网上下载最新版Maven并解压
将Maven的bin
目录添加到环境变量中.
运行mvn --version
进行测试
最近选择: 当传递依赖冲突时, 依赖树中离项目最近的依赖的版本被选择
最终会使用1.0版本的D依赖
我们也可以声明直接依赖来解决冲突问题.
依赖管理(见2.2.2小节): 依赖管理除了可以指定未声明版本的直接依赖的版本外, 传递依赖的版本可以通过dependencyManagement
强制确定.
注意, 该项目的直接依赖和继承依赖不能被依赖管理影响.
排除依赖(见2.2.1小节): 通过exclusion
元素可以排除依赖, 如A->B->D, 可以在A中排除D依赖.
依赖作用域(scope
): 声明直接依赖时可以指定它的作用域, scope
不仅影响该依赖, 还影响该依赖的传递依赖的有效作用域. 具体规则如下所示:
compile | provided | runtime | test | |
---|---|---|---|---|
compile | compile(*) | - | runtime | - |
provided | provided | - | provided | - |
runtime | runtime | - | runtime | - |
test | test | - | test | - |
行表示依赖的
scope
, 列表示传递依赖的scope
, 值为传递依赖的有效作用域
设置直接依赖的不同scope
, 来影响传递依赖的有效scope
来达到解决冲突的目录, 但是因为不太直观, 因此不建议使用该方法.
dependencyManagement
元素主要用于管理依赖的版本, 以至于声明依赖时不用写版本号. 通过我们会通过父pom来集中管理依赖, 然后子pom通过继承来获取该元素.
当有多个版本管理pom文件需要引入时会出现问题, 因为父pom只能存在一个. 可以通过scope
元素的import
值引入, 如下所示:
project ...>
<modelVersion>4.0.0modelVersion>
<groupId>baeldunggroupId>
<artifactId>TestartifactId>
<version>0.0.1-SNAPSHOTversion>
<packaging>pompackaging>
<name>Testname>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>baeldunggroupId>
<artifactId>Baeldung-BOMartifactId>
<version>0.0.1-SNAPSHOTversion>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
project>
import
只能在dependencyManagement
元素使用, 并且引入的依赖为pom
类型. 上面的例子中引入了Baeldung-BOM
的dependencyManagement
元素来进行依赖管理.
这种方法还是和继承有区别的, import
方法仅"继承"dependencyManagement
元素.
当依赖在多个地方声明版本时, 到低使用哪个版本呢? 下面给出答案, 优先级由高到低:
import
)pom的版本号, 也考虑引入pom间的顺序Maven Pom Reference
Apache Maven Tutorial
Multi-Module Project with Maven
Spring with Maven BOM
maven module inheritance vs aggregation
Dependency Mechanism: 依赖机制进阶内容