pom 的英文全拼为 Project Object Model ,项目对象模型。通过 xml 可扩展标记语言格式保存 pom.xml 文件,功能更强大。该文件可用于管理:源代码、配置文件、开发者的信息和角色、问题追踪系统、组织信息、项目授权、项目的 url 、项目的依赖关系等。
一个完整的 pom.xml 文件,放置在项目的根目录下,包含的具体内容如下所示:
4.0.0
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
粘来即用,关于这三个标签的详细说明可以参考这篇博文《XML文档中的xmlns、xmlns:xsi和xsi:schemaLocation理解》
标签名称 | 标签解释 |
xmlns | 命名空间,类似包名,因为 xml 的标签可以自定义,需要命名空间来定位 |
xmlns:xsi | xml 需要遵循的标签规范 |
xsi:schemaLocation | 用来定义xml schemaLocation 的地址,也就是 xml 书写时需要遵循的语法 |
其中 groupId、artifactId、version、packaging 这四项组成了项目的唯一坐标,一般情况下,前面的三项足矣。
4.0.0
com
SpringBoot
0.0.1-SNAPSHOT
jar
myName
www.funtl.com
标签名称 | 标签解释 |
modelVersion | 声明项目描述遵循哪一个 POM 模型版本。模型本身的版本很少改变,虽然如此,但他仍然是必不可少的,这是为了当 Maven 引入新的特性或者其他模型变更的时候,确保稳定性。 |
groupId | 公司或组织的唯一标识,并且配置时生效的路径也是由此生成,如 com.winner.trade,maven 会将项目打成的 jar 包放本地路径:/com/winner/trade 下。 |
artifactId | 本项目的唯一 ID ,一个 groupId 下面可能有多个项目,就是靠 artifactld 来区分的。 |
version | 本项目目前所处的版本号 |
packaging | 打包类型,可取值:pom、jar、war、ejb、par、rar、ear、maven-plugin |
name | 项目的名称,maven 产生的文档需要用到,可省略 |
url | 项目主页的 url ,maven 产生的文档需要用到,可省略 |
主要是用于 pom 文件之间的复用关系和依赖关系。
该元素描述了项目相关的所有依赖。这些依赖组成了项目构建过程中的一个个环节。它们自动从项目定义的仓库中下载。
junit
junit
4.0
test
true
xxx
xxx
…
标签名称 | 标签解释 |
groupId | 依赖项的组织名 |
artifactId | 依赖项的子项目名 |
version | 依赖项的版本 |
type | 依赖类型一般省略,默认类型是 jar,其他还有 jar、war、ejb-client 和 test-jar |
scope | 依赖项的适用范围 ,包括 compile、provided、runtime、test、system 和 exclusions |
optional | 可选依赖,如果你在项目 B 中把 C 依赖声明为可选,你就需要在依赖于 B 的项目(例如项目 A )中显式的引用对 C 的依赖。 |
exclusions | 排除项目中的依赖冲突时使用,不依赖该项目 |
3.1.1、scope 依赖项的适用范围:
1、compile:缺省值,适用于所有阶段,会随着项目一起发布。
2、provided:类似于 compile,期望 JDK、容器或使用者会提供这个依赖,如:servlet.jar。
3、runtime:只在运行时使用,如 JDBC 驱动,不会随项目发布。
4、system:类似 provided,需要显示提供包含依赖的 jar, Maven不会在 Repository 中查找它。
5、optional:当项目自身被依赖时,标注依赖是否传递,用于连续依赖时使用。
我们都知道,maven 的依赖关系是有传递性的。如:A -> B,B -> C。但有时候,项目 A 可能不是必须依赖 C,因此需要在项目 A 中排除对 C 的依赖,在 maven 的依赖管理中,有两种方式可以对依赖关系进行管理。分别是可选依赖(Optional Dependencies)以及依赖排除(Dependency Exclusions)。
3.1.2、可选依赖
当一个项目 A 依赖另一个项目 B 时,项目 A 可能用到了项目 B 很少一部分功能,此时就可以在 A 中配置对 B 的可选依赖。举例来说,项目 B 类似 hibernate,它支持对 mysql、oracle 等各种数据库的支持。但是在引用这个项目时,我们可能只用到其对 mysql 的支持,此时就可以在 A 项目中配置对项目 B 的可选依赖。
配置可选依赖的原因:1、节约磁盘、内存等空间;2、避免 license 许可问题;3、避免类路径问题,等等。
示例代码如下:
...
sample.ProjectB
Project-B
1.0
compile
true
假设以上配置是项目 A 的配置,即项目 A 依赖于项目 B。在编译项目 A 时,是可以正常通过的。 如果有一个新的项目 C 依赖项目 A,即 C 依赖于 A,A 依赖于 B,此时项目 C 就不会依赖项目 B 了。如果项目 C 用到了涉及项目 B 的功能,那么就需要在 pom.xml 中重新配置对项目 B 的依赖。
3.1.3、依赖排除
情况一:
当一个项目 A 依赖项目 B,而项目 B 同时依赖项目 C,如果项目 A 中因为各种原因不想引用项目 C,在配置项目 B 的依赖时,可以排除对 C 的依赖。
假设配置的是项目 A 的 pom.xml,依赖关系为:A 依赖 B,B 依赖 C。示例代码如下:
...
sample.ProjectB
Project-B
1.0
compile
sample.ProjectC
Project-C
当然,对于多重依赖,配置也很简单,参考如下示例,假设 A 依赖于B,B 依赖于 D,D 依赖于 E,E 依赖于 F,F 依赖于 C,A 对于 E 相当于有多重依赖,我们在排除对 E 的依赖时,只需要在配置 B 的依赖中进行即可:
4.0.0
sample.ProjectA
Project-A
1.0-SNAPSHOT
jar
...
sample.ProjectB
Project-B
1.0-SNAPSHOT
sample.ProjectE
Project-E
情况二:
如果我们的项目有两个依赖项:A & B,而且 A 和 B 同时依赖了 C,但 C 不是同一个版本。那么我们怎么办呢?
首先我们需要添加检查插件,代码如下所示,添加代码之后,运行:mvn project-info-reports:dependencies,来查看依赖项报告。
org.apache.maven.plugins
maven-project-info-reports-plugin
然后我们去除依赖项,代码如下所示:
org.apache.struts
struts2-core
${struts.version}
org.freemarker
freemarker
在我们已经聚合的项目中,有很多重复的配置,有相同的 groupId 和 version,有相同的spring-core,spring-beans,spring-context 和 juit 依赖,造成大量的浪费也会引发问题,所以如何使用继承机制来统一配置这些重复的信息,做到 ” 一处声明,处处使用 “ 呢?
这个时候就需要创建 pom 的父子结构,在父 pom 中声明一些配置供子 pom 继承。
父 pom.xml 的内容如下所示:
[...]
junit
junit
4.4
test
[...]
子 pom.xml 的内容如下所示,relativePath 默认为 …/pom.xml,如果路径不一样需要手动指定。
[...]
com.devzuz.mvnbook.proficio
proficio
1.0-SNAPSHOT
../ParentProject/pom.xml
[...]
我们想要一次构建两个项目,而不是到两个模块的目录下分别执行 mvn 命令 – Maven 的聚合就是为该需求服务的。为了能够使用一条命令就能构建 account-email 和 account-persist 两个模块,需要创建一个额外的名为 account-aggregator 的模块,然后通过该模块构建整个项目的所有模块。
account-aggregator 项目的 pom.xml 的内容如下所示:
4.0.0
com.park.mvnDemo.account
account-aggregator
1.0.0-SNAPSHOT
pom
Account Aggregator
account-email
account-persist
在上面的 xml 文件中,packaging 的方式为 pom。对于聚合模块来说, 其打包方式必须为pom,否则无法构建!
modules:这里是实现聚合的最核心的配置,可以声明任意数量的 module 元素来实现元素的聚合;其中,每个 module 的值为当前聚合 pom 的相对路径。
如:当前的聚合 pom 位于 D:\m2\code\account-aggregator\ ,项目 A 位于D:\m2\code\account-aggregator\account-email\,一个项目 B 位于 D:\m2\code\-aggregatoraccount\account-persist\,与上面聚合 pom 内的的 module 值相对应。也可写成 /account-email。
为了方便用户构建项目,通常将聚合模块放在项目目录的最顶层,其他模块则作为聚合模块的子目录存在。当然,你也可以手动指定路径。
主要有两个常用内置属性,如下所示:
标签名称 | 标签解释 |
${basedir} | 项目的根目录(包含 pom.xml 文件的目录) |
${version} | 项目版本 |
用户可以使用该属性引用 pom 文件中对应元素的值,常用的 pom 属性包括:
标签名称 | 标签解释 |
${project.build.sourceDirectory} | 项目的主源码目录,默认为 src/main/java |
${project.build.testSourceDirectory} | 项目的测试源码目录,默认为 src/test/java |
${project.build.directory} | 项目构件输出目录,默认为 target/ |
${project.outputDirectory} | 项目主代码编译输出目录,默认为 target/classes/ |
${project.testOutputDirectory} | 项目测试代码编译输出目录,默认为 target/test-classes/ |
${project.groupId} | 项目的 groupId |
${project.artifactId} | 项目的 artifactId |
${project.version} | 项目的 version,与 ${version} 等价 |
${project.build.fianlName} | 项目打包输出文件的名称,默认为 " ${project.artifactId} - ${project.version} " |
用户可以在 pom 的元素下自定义 Maven 属性,示例代码如下,然后我们可以在 pom 文件中通过 ${zipkin.version} 来使用他们。
1.8
UTF-8
UTF-8
Finchley.RELEASE
2.0.1
2.10.1
4.3.1 dependencies 和 dependencyManagement 标签:
在通常情况下,由于我们的模块很多,我们需要一个 itoken-denpendencies 的项目来管理子项目的公共的依赖。为了项目的正确运行,必须让所有的子项目使用依赖项的统一版本,必须确保应用的各个项目的依赖项和版本一致,才能保证测试的和发布的是相同的结果。
在我们项目顶层的 pom 文件中,我们会看到 dependencyManagement 元素。通过它元素来管理 jar 包的版本,让子项目中引用一个依赖而不用显示出具体的版本号。结合 maven 的自定义属性,我们来看看项目中的具体应用:
下面是 itoken-denpendencies 中的 pom.xml ,在父项目中用 dependencyManagement 声明依赖的版本:
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import
io.zipkin.java
zipkin
${zipkin.version}
[...]
在子项目中,Maven 会沿着父子层次向上走,直到找到一个拥有 dependencyManagement 元素的项目,然后它就会使用在这个 dependencyManagement 元素中指定的版本号。 itoken-zipkin 中的 pom.xml 内容如下所示:
org.springframework.cloud
spring-cloud-dependencies
io.zipkin.java
zipkin
[...]
需要注意的是,dependencyManagement 里只是声明依赖,并不实现引入。如果不在子项目中声明依赖,是不会从父项目中继承下来的;只有在子项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承该项,并且 version 和 scope 都读取自父 pom。如果子项目中指定了版本号,那么会使用子项目中指定的 jar 版本。
用户使用 ${settings.} 开头的属性可以引用 maven settings.xml 文件中 xml 元素的值,如下所示:
标签名称 | 标签解释 |
settings.localRepository | 自定义本地库路径,默认在 ${user.home}/.m2 中 |
settings.interactiveMode | 交互模式,Maven 是否应该尝试与用户输入交互,默认是 true,如果不是 false。 |
settings.offline | 是否每次编译都去查找远程中心库, 如果此构建系统应以离线模式运行,则为 true,默认为 false。由于网络设置或安全原因,此元素对于构建无法连接到远程存储库的服务器非常有用。 |
settings.pluginGroups | 插件组,例如 org.mortbay.jetty |
所有 Java 系统属性都可以使用 Maven 属性引用,这里列举一些常用的 Java 系统属性。
标签名称 | 标签解释 |
java.version | Java 运行时环境版本 |
java.vendor | Java 运行时环境供应商 |
java.vendor.url | Java 供应商的 URL |
java.home | Java 安装目录 |
java.vm.specification.version | Java 虚拟机规范版本 |
java.vm.specification.vendor | Java 虚拟机规范供应商 |
java.vm.specification.name | Java 虚拟机规范名称 |
java.vm.version | Java 虚拟机实现版本 |
java.vm.vendor | Java 虚拟机实现供应商 |
java.vm.name | Java 虚拟机实现名称 |
java.class.path | Java 类路径 |
os.name | 操作系统的名称 |
file.separator | 文件分隔符(在 UNIX 系统中是“/”) |
user.name | 用户的账户名称 |
user.home | 用户的主目录 |
user.dir | 用户的当前工作目录 |
所有环境变量都可以使用以 env. 开头的 Maven 属性引用。比如:${env.JAVA_HOME} 表示环境变量的值。
build 标签可以是单独的,也可以是包含在 profile 标签里面,如下所示:
…
…
build 标签包含两个子标签,分别是 Resources 标签和 Plugins 标签,下面分别介绍下这两个标签。
资源文件管理,用于引用或者排除资源文件,如下所示:
META-INF/plexus
false
${basedir}/src/main/plexus
configuration.xml
**/*.properties
标签名称 | 标签解释 |
targetPath | 资源的打包路径,该路径相对 target/classes 目录 |
filtering | 主要用来替换项目中的资源文件(* .xml、* . properties)当中的 $ {…} 属性值如 $ {db.url} 如果 filtering=true 在 resources 目录中存在资源文件并且配置了 db.url=aaa 的话, 在项目编译的时候,就会自动的把 pom 文件中的 ${db.url} 替换为 aaa |
directory | 描述存放资源的目录,该路径相对 pom 的路径 |
includes | 包含的模式列表,例如 **/*.xml |
excludes | 排除的模式列表,例如 **/*.xml |
设置构建的插件,为什么我们需要插件?
第一个原因是我们需要某个特殊的 jar 包,但是又不能直接通过 maven 依赖获取,或者说在其他环境的 maven 仓库内不存在,那么如何将我们所需要的 jar 包打入我们的生产 jar 包中。就需要这个插件。
第二个原因是某个 jar 包内部包含的文件是我们所需要的,或者是我们希望将它提取出来放入指定的位置 ,那么除了复制粘贴,也可以通过 maven 插件实现。
5.2.1 plugins 和 pluginmanagement
maven 通过 plugins 和 pluginmanagement 来管理插件,类似 dependencies 和 dependencyment 。但需要注意的是,plugins 和 pluginmanagement 隶属于 build 标签下,而不是最外层的 project 标签。
在父项目中声明插件和版本信息,同样 pluginmanagement 也不会直接引入插件。
org.apache.maven.plugins
maven-source-plugin
2.1
true
compile
jar
在子项目中引入插件,同样,子项目继承父项目的 plugin 设置,并可以自由定义。
org.apache.maven.plugins
maven-source-plugin
Maven 仓库就是放置所有 JAR 文件(WAR,ZIP,POM 等等)的地方,所有 Maven 项目可以从同一个 Maven 仓库中获取自己所需要的依赖 JAR,这节省了磁盘资源。此外,由于 Maven 仓库中所有的 JAR 都有其自己的坐标,该坐标告诉 Maven 它的组件 ID,构件 ID,版本,打包方式等等,因此 Maven 项目可以方便的进行依赖版本管理。你也不再需要提交 JAR 文件到 SCM 仓库中,你可以建立一个组织层次的 Maven 仓库,供所有成员使用。
简言之,Maven仓库能帮助我们管理构件(主要是JAR)。maven 寻找仓库的顺序大致可以理解为:
1、在本地仓库中寻找,如果没有则进入下一步。
2、在全局应用的私服仓库中寻找,如果没有则进入下一步。
3、在项目自身的私服仓库中寻找,如果没有则进入下一步。
4、在中央仓库中寻找,如果没有则终止寻找。
补充:
1、如果在找寻的过程中,如果发现该仓库有镜像设置,则用镜像的地址代替。
2、如果仓库的 id 设置成 “ central ” ,则该配置会覆盖 maven 默认的中央仓库配置。
Maven 缺省的本地仓库地址为 ${user.home}/.m2/repository 。也就是说,一个用户会对应的拥有一个本地仓库。你也可以自定义本地仓库的位置,修改 ${user.home}/.m2/settings.xml 。
D:/repo
也可以在运行时指定本地仓库位置:
mvn clean install -Dmaven.repo.local=/home/juven/myrepo/
3.xxx 版本之后,在 maven 安装目录下的:/lib/maven-model-builder-${version}.jar 中
打开该文件,能找到超级 pom :\org\apache\maven\model\pom-4.0.0.xml,它是所有 Maven pom 的父 pom ,所有 Maven 项目继承该配置,你可以在这个 pom 中发现如下配置:
central
Central Repository
https://repo.maven.apache.org/maven2
default
false
中央仓库的 id 为 central,远程 url 地址为 http://repo.maven.apache.org/maven2 ,它关闭了 snapshot 快照版本构件下载的支持。
前面我们看到超级 pom 配置了 ID 为 central 的远程仓库,我们可以在 pom 中配置其它的远程仓库。这样做的原因有很多,比如你有一个局域网的远程仓库,使用该仓库能大大提高下载速度,继而提高构建速度,也有可能你依赖的一个 jar 在 central 中找不到,它只存在于某个特定的公共仓库,这样你也不得不添加那个远程仓库的配置。这里我配置一个远程仓库指向中央仓库的中国镜像:
...
maven-net-cn
Maven China Mirror
http://maven.net.cn/content/groups/public/
true
false
maven-net-cn
Maven China Mirror
http://maven.net.cn/content/groups/public/
true
false
...
我们先看一下它的配置,你可以在它下面添加多个 ,每个都有它唯一的 ID,一个描述性的 name,以及最重要的,远程仓库的 url 。此外:
true
false
禁止从公共仓库下载 snapshot 构件是推荐的做法,因为这些构件不稳定,且不受你控制,你应该避免使用。当然,如果你想使用局域网内组织内部的仓库,你可以激活 snapshot 的支持。