本文以当前最新发布版maven-3.6.3
为例.酌情参考.
得益于坐标机制,任何Maven
项目使用任何一个构建的方式都是完全相同的。在此基础上,Maven
可以在某个位置统一存储所有Maven
项目共享的构件。这个统一的位置就是仓库。实际的Maven
项目将不再各自存储其依赖文件,它们只需要声明这些依赖的坐标,在需要的时候Maven
会自动根据坐标找到相应的构件。
Maven
仓库的分类不能一概而论,根据不同的角度可以大致罗列如下:
《Maven实战》中将仓库分为:
本地仓库主要起缓存作用,默认在~/.m2/repository
。
可以通过~/.m2/settings.xml
中的
修改。
根据是否发布,可以分为SNAPSHOT
和RELEASE
两类。
私服Nexus中将仓库分类为hosted
,group
和proxy
。
一般只包含SNAPSHOT
和RELEASE
两类。因此Nexus内置了maven-snapshots
和maven-releases
。
顾名思义就是组,可以自由组合,类型不限。Nexus内置了maven-public
。
proxy
只能一次代理一个目标仓库。一般都是将生成某一类proxy
地址归并到一个group
中,形成类似Nginx
反向代理的效果。
proxy
类型应该是私服
用的最多的类型。
Nexus内置了maven-central
,它默认代理的是https://repo1.maven.org/maven2/。
Maven
的目录结构看起来比较简单,包括:
├── bin
├── boot
├── conf
│ └── logging
└── lib
├── ext
└── jansi-native
├── freebsd32
├── freebsd64
├── linux32
├── linux64
├── osx
├── windows32
└── windows64
bin主要存放命令行的执行脚本,包括mvn
和mvnDebug
。此外还有m2.conf
文件.这是classworlds
的配置文件.
只有一个文件plexus-classworlds-x.x.x.jar
,是一个类加载器框架.一般不用关心.
配置文件夹.包含最重要的settings.xml
.
用以存放maven
运行时需要的Java
类库.包含maven
自身及第三方依赖.
在当前版本3.6.3
中,该目录下的maven-model-builder-3.6.3.jar
的org.maven.model
下存放了全局的pom-4.0.0.xml
,它定义了默认的central
仓库链接:https://repo.maven.apache.org/maven2
.
指的是pom.xml
最外一层的xml
元素,常用的包括groupId
,artifactId
,version
,packaging
,dependencyManagement
和build
.
该元素定义Maven
项目的打包方式.首先,打包方式通常与所生成构件的文件扩展名称对应.默认为jar
,可供选类型包括但不限于jar
,war
,ear
和pom
.之所以不限是因为打包插件可以自定义类型.
依赖坐标是最最常用的元素了,这里指对应pom.xml
中
.
包括groupId
,artifactId
,version
,classifier
,type
和exclusions
.
一个依赖坐标对应的文件格式一般为$artifactId-$version-$classifier
,扩展名一般是$type
,它的默认值是jar
,不过尽管它通常表示依赖项文件名的扩展名,但并非总是如此。 可以将类型映射到其他扩展名和classifier
。 这种类型通常对应于所使用的包装,尽管并非总是如此。 可供选项包括jar
,war
,ejb-client
和test-jar
。 可以通过将扩展名设置为true
的插件来自定义新类型,因此这不是完整列表。
帮助定义构件输出的一些附属构件.比如文档和源代码.对应的classifier
就分别是javadoc
和sources
.
另外,常用的选项还有pom
,通常表示一些父依赖.
比较经典的应用是net.sf.json-lib:json-lib
,必须指定classifier
才可正常工作.
<dependency>
<groupId>net.sf.json-libgroupId>
<artifactId>json-libartifactId>
<version>2.4version>
<classifier>jdk15classifier>
dependency>
注意:
不能直接定义项目的
classifier
因为附属构件不是项目直接默认生成的,而是由附加的插件帮助生成.
依赖范围:
依赖范围 | 编译 | 测试 | 运行 | 例子 |
---|---|---|---|---|
compile |
Y | Y | Y | spring-core |
test |
- | Y | - | Junit |
provided |
Y | Y | - | servlet-api |
runtime |
- | Y | Y | oracle-jdbc驱动 |
system |
Y | Y | - | 本地的,Maven仓库之外的类库文件 与 搭配使用 |
optional |
Y | N | N | 可选,并且不会被传递依赖 1. spring-boot-devtools 2. 比如有个持久层隔离包,里面有多个数据库驱动程序,它们是互斥的,放在 pom 中供调用者参考. |
$MAVEN_HOME/conf/settings.xml
是全局配置文件。一般不建议修改。
当我们初次执行操作,如mvn help:system
时会创建~/.m2
和repository
目录,同时生成空的settings.xml
用以存放用户级别的配置。用户根据需要修改~/.m2/settings.xml
。
<settings>
<localRepository>/path/to/local/repolocalRepository>
<interactiveMode>trueinteractiveMode>
<offline>falseoffline>
<pluginGroups> pluginGroups>
<proxies>proxies>
<servers>servers>
<mirrors> mirrors>
<profiles> profiles>
<activeProfiles> activeProfiles>
settings>
用以指定本地仓库位置.
当插件的组织Id(groupId)
没有显式提供时,供搜寻插件组织Id(groupId)
的列表。该元素包含一个pluginGroup
元素列表,每个子元素包含了一个组织Id(groupId)
。当我们使用某个插件,并且没有在命令行为其提供组织Id(groupId)
的时候,Maven
就会使用该列表。默认情况下该列表包含了org.apache.maven.plugins
和org.codehaus.mojo
.
有时候所在公司基于安全考虑,要求使用通过安全认证的代理访问互联网.这时候就需要为其配置HTTP
代理,才能正常访问和下载.可供配置标签:
<proxies>
<proxy>
<id>optionalid>
<active>trueactive>
<protocol>httpprotocol>
<username>proxyuserusername>
<password>proxypasspassword>
<host>proxy.host.nethost>
<port>80port>
<nonProxyHosts>local.net|some.host.comnonProxyHosts>
proxy>
proxies>
proxies
下可以有多个proxy
元素,如果声明了多个,则默认情况下第一个被激活的会生效.
nonProxyHosts
用来指定哪些主机不需要被代理.可以使用|
符号来分隔多个,同时支持通配符*
,比如*.aliyun.com
.
用来配置远程仓库的认证信息,其中server
的id
与pom.xml
中的repository
的id
一一对应.
<servers>
<server>
<id>swbid>
<username>adminusername>
<password>XXXXpassword>
<privateKey>~/.ssh/id_rsaprivateKey>
<passphrase>XXXpassphrase>
server>
servers>
有些仓库在国内访问速度较慢,因此会出现一些镜像仓库,如阿里云等,mirrors
就是用来配置镜像仓库提高效率。
以公有仓库为例:
<mirror>
<id>aliyunid>
<mirrorOf>publicmirrorOf>
<name>aliyunname>
<url>https://maven.aliyun.com/repository/publicurl>
mirror>
由上面的分类可知,Maven有好几种类型仓库
这里的mirrorOf
支持通配符*
,代表所有类型,如果有私服的话,可以只配一个镜像地址,省事儿,比如我们的私服结构如下:
public
代理了绝大部分常用仓库,而且非常容易扩展,此时使用
就非常合适。
类似springboot
中的profile
,针对不同的环境开启不同的配置,比如在settings.xml
中设置统一的默认编译版本:
<profiles>
<profile>
<id>java8id>
<activation>
<activeByDefault>trueactiveByDefault>
<jdk>1.8jdk>
activation>
<properties>
<maven.compiler.source>1.8maven.compiler.source>
<maven.compiler.target>1.8maven.compiler.target>
<maven.compiler.compilerVersion>1.8maven.compiler.compilerVersion>
properties>
profile>
<profile>
<id>java9id>
<activation>
<activeByDefault>falseactiveByDefault>
<jdk>9jdk>
activation>
<properties>
<maven.compiler.source>9maven.compiler.source>
<maven.compiler.target>9maven.compiler.target>
<maven.compiler.compilerVersion>9maven.compiler.compilerVersion>
properties>
profile>
profiles>
<activeProfiles>
<activeProfile>java8activeProfile>
activeProfiles>
除此之外,还可以通过命令行-P
参数指定:mvn compile -Pjava8
.
就像make
的Makefile
,Ant
的build.xml
一样,pom.xml
是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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>org.examplegroupId>
<artifactId>mavenartifactId>
<version>1.0-SNAPSHOTversion>
<dependencies>dependencies>
project>
有些情况下,默认的中央仓库无法满足项目的需求,可能项目需要的构件存在于另外一个远程仓库中,如JBoss Maven
.这时,可以在POM
中配置该仓库.
在repositories
元素下,可以使用repository
子元素声明一个或者多个远程仓库.比如在超级POM
(位于maven-model-builder-3.6.3.jar
)中默认的仓库配置:
<repositories>
<repository>
<id>centralid>
<name>Central Repositoryname>
<url>https://repo.maven.apache.org/maven2url>
<layout>defaultlayout>
<snapshots>
<enabled>falseenabled>
snapshots>
repository>
repositories>
使用的id
是central
,如果其他仓库声明的id
也一样,就会覆盖中央仓库的配置.
这里的snapshots
比较重要,定义了是否提供快照版本下载支持.对应的还有releases
,它们都是对应的xsd
规定的RepositoryPolicy
类型,默认为真.
RepositoryPolicy
还包括updatePolicy
和checksumPolicy
,表示更新频率和文件校验策略.
默认的值是daily
,表示每天检查一次.其他可用的值包括:
never
-从不检查更新always
-每次构建都检查更新interval: X
-每隔X分钟就检查一次更新,X为任意整数.warn
,默认值,校验不通过给出提示ignore
,忽略fail
,校验不通过构建失败.表示布局,两个可选项:legacy
和default
.
legacy
对应的是maven1
的布局,一般不用.
default
对应的是maven2
和maven3
的布局,是目前来说最常用的,因此也是默认值.
有时出于安全考虑,需要认证后提供下载和部署服务,比如很多公司内部的私服不允许匿名拉取文件的.
但认证信息在settings.xml
中servers
的子元素server
中配置,通过id
与此处的repository
的id
一一对应.
除了全局的settings.xml
和项目级别pom.xml
,还有其他一些配置,比如MAVEN_OPTS
,项目根目录下的.mvn
文件夹.具体参考官网configure
Maven
拥有三套相互独立的生命周期:clean
,default
和site
.
每个生命周期都包含多个阶段(phase
),这些阶段是有顺序的.
目的是清理项目,包含三个阶段:.
阶段 | 可执行 |
---|---|
pre-clean |
F |
clean |
T |
post-clean |
F |
定义了真正构件时所需要执行的所有步骤,它是所有生命周期中最核心的部分.它包含的阶段较多,这里仅仅列举一些关键阶段,它们均可在命令行显式执行:
validate
验证项目是否正确并且所有必要的信息均可用compile
test
package
verify
对集成测试的结果进行任何检查,以确保符合质量标准install
将生成的包安装到本地存储库中,以作为本地其他项目中的依赖项deploy
在构建环境中完成后,将最终程序包部署到远程存储库,以便与其他开发人员和项目共享。目的是建立和发布项目站点,Maven
能够基于POM
所包含的信息,执行mvn site
命令会自动生成一个友好的站点,位于target/site
目录下的静态页面,包含如下阶段:
pre-site
site
post-site
site-deploy
将生成的项目站点发布到服务器上本生命周期不常用,用到再补充.
为了达到开箱即用的效果,Maven
内置了许多常用插件:
name | phase | more details |
maven-clean-plugin | clean | clean_Lifecycle |
maven-resources-plugin | default | default-bindings |
maven-compiler-plugin | ||
maven-surefire-plugin | ||
maven-jar-plugin | ||
maven-install-plugin | ||
maven-deploy-plugin | ||
maven-site-plugin | site | site_Lifecycle |
Maven
的核心仅仅定义了抽象的生命周期,具体的任务是交给插件完成的,插件为了功能复用,又将其分为多个目标.
比如maven-compiler-plugin
包含三个目标:
Goal | Description |
---|---|
compiler:compile | 绑定到编译阶段,用于编译主要源文件。 |
compiler:help | 在maven-compiler-plugin 上显示帮助信息。 调用mvn compiler:help -Ddetail=true -Dgoal= 以显示参数详细信息。 |
compiler:testCompile | 绑定到测试编译阶段,用于编译测试源文件。 |
如果想查看其他插件目标,直接访问官网,找到想了解的插件,查看:
Maven
的生命周期与插件相互绑定,用以完成实际的构建任务.
具体而言,是生命周期的阶段与插件目标相互绑定.
为了让用户几乎不用任何配置就能构建Maven
项目,所以内置了一些绑定,官网中给出了一系列对应关系.
其中clean
和site
阶段的绑定关系比较简单,直接列举出来了,default
阶段的默认绑定比较复杂,是根据打包类型对应的,可以查看其给出的default-bindings.
Maven
帮我们内置了一些插件,一般足够满足我们的需求,只有在很少的情况下,项目使用的插件无法在中央仓库找到,或者自己编写了插件,就必须考虑配置其他的远程插件仓库了.
pom.xml
为我们提供了条件:
使用pluginRepositories
+pluginRepository
,其他配置则与repositories
+repository
相同.
命令行可以直接激活生命周期阶段,也可以调用插件目标.
mvn clean
mvn test
mvn clean install
mvn clean deploy site-deploy
有些任务不适合绑定在生命周期上,所以Maven
提供了调用插件目标的方式:
mvn help:describe -Dplugin=compiler -Ddetail
mvn dependency:tree
describe
是插件目标没错,那help
是什么呢,其实它是目标前缀,是Maven
为了简化调用而设定的.它的完整命令是:
mvn org.apache.maven.plugins:maven-help-plugin:3.2.0:describe -Dplugin=compiler -Ddetail
其中版本号可选.第二条命令同理.
一般的前缀与它的插件名称对应:maven-XXX-plugin
,如果不一样的话,以groupId
为org.apache.maven.plugins
的官方插件为例,可以通过远程仓库的maven-metadata.xml
查看所有插件的前缀.
对于复杂的Maven
项目,一般建议采用多模块的方式来设计开发,便于后期维护管理。但是构建项目时,如果每次都需要按模块一个一个进行构建会十分麻烦,而Maven
的聚合功能就可以很好的解决这个问题,当用户对聚合模块执行构建任务时,会对所有被其聚合的模块自动地依次进行构建任务.
一个聚合项目,其packaging
必须定义为pom
.然后使用modules
列举其他模块.
父模块packaging
必须为pom
.
主要涉及的元素是properties
,dependencyManagement
和pluginManagement
.
预置属性,子模块可以直接使用.
定义一些可能涉及的不冲突的依赖,子模块在使用相同的依赖时,可以忽略版本,防止冲突.
子模块可以选择不使用,显式配置依赖时才会生效.
当子模块定义了不同的版本时,会覆盖.
与dependencyManagement
起相同作用.
如果有公共依赖,可以提取到这里,以便统一管理.
上述properties
,dependencyManagement
和pluginManagement
都是可继承元素,但这不是全部.
所有可继承元素:
groupId
version
description
organization
inceptionYear
创始年份url
developers
contributors
distributionManagement
部署配置issueManagement
缺陷跟踪ciManagement
持续集成(Continuous Integration
)管理scm
版本控制信息mailingLists
邮件列表信息properties
dependencies
dependencyManagement
pluginManagement
repositories
build
reporting
通过parent
引入就可以享受父模块提供的便利.
parent
中有一个relativePath
元素用于指定父模块的pom.xml
位置,默认值是../pom.xml
,如果不符合这种结构,可以另行指定.
我们在一个新的项目/模块中,如果期望复用其他POM
中dependencyManagement
元素的所有配置,我们可以通过继承或拷贝该配置实现.
因为maven
只支持单继承,如果当前项目已经继承了一个父模块,此时即可通过import
导入的方式来复用其他POM
中的dependencyManagement
配置。需要注意的是,由于import
依赖范围的特殊性,其一般指向的是打包类型为pom
的模块。故其type只能为pom.
<parent>
<groupId>com.yangroupId>
<artifactId>xxx-parentartifactId>
<version>xxxxversion>
<relativePath/>
parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.4.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
packaging
均为pom
,这意味着项目可以同时是聚合模块和父模块.就是决定构建顺序的构建结构.
单模块的反应堆就是它本身,如果有父模块,先构建父模块.
如果是聚合模块,那么module
定义的顺序决定了构建顺序.
大多数时候,用户都是一次性构建整个项目,但有些时候用户可能仅仅需要构建某个模块,即用户需要裁剪反应堆来实现构建指定模块的目的。在Maven
命令行中可通过相关选项参数实现反应堆的裁剪
-pl
,projects
:构建指定模块,多个模块用逗号进行分隔
mvn package -pl m_a, m_b
-am
,-also-make
:同时构建所列模块的父模块
mvn package -pl m_a -am
-amd
,-also-make-dependents
:同时子模块
mvn package m_parent -amd
-rf
,-resume-from
:从指定模块开始构建
mvn -rf
主要有两个常用内置属性:
basedir
:项目根目录,即包含pom.xml
的目录version
:表示项目版本project.build.sourceDirectory
:项目的主源码目录,默认为src/main/java
.project.build.testSourceDirectory
:项目的测试源码目录,默认为src/test/java
.project.build.directory
:项目构建输出目录,默认为target
.project.outputDirectory
:项目主代码编译输出目录,默认为target/classes
project.testOutputDirectory
:项目测试代码编译输出目录,默认为target/testclasses
project.groupId
project.artifactId
project.version
project.build.finalName
:项目打包输出文件的名称,默认为${project.artifactId}-${project.version}
用户可以在POM
的properties
元素下自定义属性
可以用以settings.
开头的属性引用settings.xml
中的值,如settings.localRepository
指向本地仓库的地址.
所有Java系统都可以使用Maven
属性引用.使用mvn help:system
查看所有的Java系统属性.
所有环境变量都可以使用以env.
开头的Maven
属性引用.使用mvn help:system
查看所有的环境变量属性.
Maven
属性默认只有在POM
中才会被解析,因此有时会需要让Maven
解析资源文件(位于src/main/resources
)中的Maven
属性.
资源文件的处理需要通过maven-resources-plugin
,它默认的行为只是将项目主资源文件复制到主代码编译输出目录中,将测试资源文件复制到测试代码编译输出目录中.若要开启资源过滤,需要配置
为真:
<plugin>
<artifactId>maven-resources-pluginartifactId>
<configuration>
<resources>
<resource>
<directory>${project.build.sourceDirectory}directory>
<filtering>truefiltering>
resource>
resources>
configuration>
plugin>
默认路径是~/.m2/toolchains.xml
.
工具链是一个预配置的对象,Maven
插件可将其用于工具配置检索(位置和其他信息)。
最常用的就是Jdk
版本的选择,还提供了自定义工具链的方法.
使用工具链,需要配置两个基本组件:
POM
中的maven-toolchains-plugin
,toolchains.xml
文件。Jdk
版本工具链示例:
在~/.m2/toolchains.xml
中:
<toolchains>
<toolchain>
<type>jdktype>
<provides>
<version>1.5version>
<vendor>sunvendor>
provides>
<configuration>
<jdkHome>/path/to/jdk/1.5jdkHome>
configuration>
toolchain>
<toolchain>
<type>jdktype>
<provides>
<version>1.6version>
<vendor>sunvendor>
provides>
<configuration>
<jdkHome>/path/to/jdk/1.6jdkHome>
configuration>
toolchain>
<toolchain>
<type>netbeanstype>
<provides>
<version>5.5version>
provides>
<configuration>
<installDir>/path/to/netbeans/5.5installDir>
configuration>
toolchain>
toolchains>
pom.xml
中
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.1version>
plugin>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-toolchains-pluginartifactId>
<version>1.1version>
<executions>
<execution>
<goals>
<goal>toolchaingoal>
goals>
execution>
executions>
<configuration>
<toolchains>
<jdk>
<version>1.5version>
<vendor>sunvendor>
jdk>
toolchains>
configuration>
plugin>
plugins>
3.3.1版本开始可以使用--global-toolchains file
来指定toolchains
的配置位置,但还是强烈建议放在默认目录~/.m2
下.
maven-jar-plugin
打的jar
包不可执行,需要借助maven-shade-plugin
来配置:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-shade-pluginartifactId>
<version>3.2.0version>
<executions>
<execution>
<phase>packagephase>
<goals>
<goal>shadegoal>
goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>${你的主类全类名}mainClass>
transformer>
transformers>
configuration>
execution>
executions>
plugin>
plugins>
build>
更多信息参考:maven-shade-plugin.
在pom.xml
中加入
<distributionManagement>
<repository>
<id>swbid>
<url>http://repo.cnswb.com/repository/maven-releases/url>
repository>
<snapshotRepository>
<id>swbid>
<url>http://repo.cnswb.com/repository/maven-snapshots/url>
snapshotRepository>
distributionManagement>
执行命令mvn clean deploy
,如果当前项目版本为快照版本则发布至snapshotRepository
,否则是repository
.
普通升级都是从官网下载最新安装包到本机解压后从新指定环境变量PATH
。
但还有更好的方式,即创建一个软连接用来统一代理,这样每次只需改变软连接的目标地址实现升级了。
在/opt/apache
目录下创建一个软连接maven
,用它来指向想要升级的Maven
目录,环境变量中的M2_HOME
设置为/opt/apache/maven
。
比如从当前为3.6.1
:
ln -s /opt/apache/apache-maven-3.6.1 /opt/apache/maven
升级为3.6.3
:
rm -rf /opt/apache/maven
ln -s /opt/apache/apache-maven-3.6.3 /opt/apache/maven
升级后查看:
yan@yan-PC:/opt$ ll /opt/apache
总用量 16
drwxr-xr-x 4 yan yan 4096 3月 25 14:37 .
drwxr-xr-x 26 yan yan 4096 3月 25 10:52 ..
drwxr-xr-x 6 yan yan 4096 3月 25 14:37 apache-maven-3.6.1
drwxr-xr-x 6 yan yan 4096 3月 25 10:57 apache-maven-3.6.3
lrwxrwxrwx 1 yan yan 30 3月 25 10:56 maven -> /opt/apache/apache-maven-3.6.3