Project Object Model
,项目对象模型。通过xml可扩展标记语言(EXtensible Markup Language)格式保存的pom.xml文件。作用类似ant的build.xml文件,功能更强大。该文件用于管理:源代码、配置文件、开发者的信息和角色、问题追踪系统、组织信息、项目授权、项目的url、项目的依赖关系等等。
一个完整的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
http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0modelVersion>
<!– The Basics –>
<groupId>…groupId>
<artifactId>…artifactId>
<version>…version>
<packaging>…packaging>
<dependencies>…dependencies>
<parent>…parent>
<dependencyManagement>…dependencyManagement>
<modules>…modules>
<properties>…properties>
<!– Build Settings –>
<build>…build>
<reporting>…reporting>
<!– More Project Information –>
<name>…name>
<description>…description>
<url>…url>
<inceptionYear>…inceptionYear>
<licenses>…licenses>
<organization>…organization>
<developers>…developers>
<contributors>…contributors>
<!– Environment Settings –>
<issueManagement>…issueManagement>
<ciManagement>…ciManagement>
<mailingLists>…mailingLists>
<scm>…scm>
<prerequisites>…prerequisites>
<repositories>…repositories>
<pluginRepositories>…pluginRepositories>
<distributionManagement>…distributionManagement>
<profiles>…profiles>
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
http://maven.apache.org/xsd/maven-4.0.0.xsd" -->
字段 | 说明 |
---|---|
xmlns |
命名空间,类似包名,因为xml的标签可自定义,需要命名空间来 |
xmlns:xsi |
xml遵循的标签规范 |
xsi:schemaLocation |
用来定义xmlschema的地址,也就是xml书写时需要遵循的语法 |
粘来即用,详细说明可以参考这篇博文 《XML文档中的xmlns、xmlns:xsi和xsi:schemaLocation理解》
<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">
<modelVersion>4.0.0modelVersion>
<groupId>com.funtlgroupId>
<artifactId>itoken-dependenciesartifactId>
<version>1.0.0version>
<packaging>warpackaging>
<name>itoken dependenciesname>
<url>www.funtl.comurl>
project>
字段 | 说明 |
---|---|
modelVersion |
声明项目描述符遵循哪一个POM模型版本。模型本身的版本很少改变,虽然如此,但它仍然是必不可少的, 这是为了当Maven引入了新的特性或者其他模型变更的时候,确保稳定性。 |
groupId |
公司或者组织的唯一标志,并且配置时生成的路径也是由此生成, 如com.winner.trade,maven会将该项目打成的jar包放本地路径:/com/winner/trade |
artifactId |
本项目的唯一ID,一个groupId下面可能多个项目,就是靠artifactId来区分的 |
version |
本项目目前所处的版本号 |
packaging |
打包类型,可取值:pom , jar , maven-plugin , ejb , war , ear , rar , par等等 |
name |
项目的名称, Maven产生的文档用,可省略 |
url |
项目主页的URL, Maven产生的文档用 ,可省略 |
等等还好好多属性,具体参见博客末尾的 完整版pom.xml
;
其中groupId,artifactId,version,packaging这四项组成了项目的唯一坐标。一般情况下,前面三项就足矣。
主要用于POM文件的复用和依赖。
该元素描述了项目相关的所有依赖。 这些依赖组成了项目构建过程中的一个个环节。它们自动从项目定义的仓库中下载。
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.0version>
<scope>testscope>
<optional>trueoptional>
<exclusions>
<exclusion>
<groupId>xxxgroupId>
<artifactId>xxxartifactId>
exclusion>
exclusions>
dependency>
…
dependencies>
字段 | 说明 |
---|---|
groupId |
依赖项的组织名 |
artifactId |
依赖项的子项目名 |
version |
依赖项的版本 |
type |
依赖类型一般省略,默认类型是jar,其他还有jar,war,ejb-client和test-jar |
scope |
依赖项的适用范围 ,包括compile,provided,runtime,test,system,exclusions [^1] |
optional |
可选依赖,如果你在项目B中把C依赖声明为可选,你就需要在依赖于B的项目(例如项目A)中显式的引用对C的依赖。 |
exclusions |
排除项目中的依赖冲突时使用,不依赖该项目 |
[^1]scope 依赖项的适用范围
我们知道,maven的依赖关系是有传递性的。如:A–>B,B–>C。但有时候,项目A可能不是必需依赖C,因此需要在项目A中排除对A的依赖。在maven的依赖管理中,有两种方式可以对依赖关系进行,分别是可选依赖(Optional Dependencies)
以及依赖排除(Dependency Exclusions)
。
当一个项目A依赖另一个项目B时,项目A可能用到了项目B很少一部分功能,此时就可以在A中配置对B的可选依赖。举例来说,项目B类似hibernate,它支持对mysql、oracle等各种数据库的支持,但是在引用这个项目时,我们可能只用到其对mysql的支持,此时就可以在A项目中配置对项目B的可选依赖。
配置可选依赖的原因:1、节约磁盘、内存等空间;2、避免license许可问题;3、避免类路径问题,等等。
示例:
<project>
...
<dependencies>
<dependency>
<groupId>sample.ProjectBgroupId>
<artifactId>Project-BartifactId>
<version>1.0version>
<scope>compilescope>
<optional>trueoptional>
dependency>
dependencies>
project>
假设以上配置是项目A的配置,即:Project-A <-- Project-B。在编译项目A时,是可以正常通过的。
如果有一个新的项目C依赖A,即:Project-C <-- Project-A <-- Project-B
。此时项目C就不会依赖项目B了。
如果项目C用到了涉及项目B的功能,那么就需要在pom.xml中重新配置对项目B的依赖。
当一个项目A依赖项目B,而项目B同时依赖项目C,如果项目A中因为各种原因不想引用项目C,在配置项目B的依赖时,可以排除对C的依赖。
示例(假设配置的是A的pom.xml,依赖关系为:A<–B<–C):
...
sample.ProjectB
Project-B
1.0
compile
sample.ProjectC
Project-C
当然,对于多重依赖,配置也很简单,参考如下示例:
Project-A
-> Project-B
-> Project-D
-> Project-E
-> Project-F
-> Project 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,但不是同一个版本。那么我们怎么办呢?
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-project-info-reports-pluginartifactId>
plugin>
plugins>
reporting>
然后运行:mvn project-info-reports:dependencies,来查看依赖项报告。
<dependency>
<groupId>org.apache.strutsgroupId>
<artifactId>struts2-coreartifactId>
<version>${struts.version}version>
<exclusions>
<exclusion>
<groupId>org.freemarkergroupId>
<artifactId>freemarkerartifactId>
exclusion>
exclusions>
dependency>
在我们已经聚合的项目中,有很多重复的配置,有相同的groupId和version,有相同的spring-core, spring-beans, spring-context和juit依赖,造成大量的浪费也会引发问题,所以如何使用继承机制来统一配置这些重复的信息,做到”一处声明,处处使用“呢?
思路:创建POM的父子结构,在父POM中声明一些配置供子POM继承、
<project>
[...]
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.4version>
<scope>testscope>
dependency>
dependencies>
[...]
project>
[...]
<parent>
<groupId>com.devzuz.mvnbook.proficiogroupId>
<artifactId>proficioartifactId>
<version>1.0-SNAPSHOTversion>
<relativePath>../ParentProject/pom.xmlrelativePath>
parent>
[...]
relativePath
默认为…/pom.xml,如果路径不一样需要手动指定
我们想要一次构建两个项目,而不是到两个模块的目录下分别执行mvn命令 – Maven的聚合就是为该需求服务的。
为了能够使用一条命令就能构建account-email和account-persist两个模块,需要创建一个额外的名为account-aggregator的模块,然后通过该模块构建整个项目的所有模块。
account-aggregator也有它自己的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.maven-v4_0_0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.park.mvnDemo.accountgroupId>
<artifactId>account-aggregatorartifactId>
<version>1.0.0-SNAPSHOTversion>
<packaging>pompackaging>
<name>Account Aggregatorname>
<modules>
<module>account-emailmodule>
<module>account-persist<module>
modules>
project>
在上面的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属性
<properties>
<java.version>1.8java.version>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<spring-cloud.version>Finchley.RELEASEspring-cloud.version>
<spring-boot-admin.version>2.0.1spring-boot-admin.version>
<zipkin.version>2.10.1zipkin.version>
properties>
然后在pom文件中通过${zipkin.version}来使用他们
在通常情况下,由于我们的模块很多,我们需要一个itoken-denpendencies的项目来管理子项目的公共的依赖。
为了项目的正确运行,必须让所有的子项目使用依赖项的统一版本,必须确保应用的各个项目的依赖项和版本一致,才能保证测试的和发布的是相同的结果。
在我们项目顶层的POM文件中,我们会看到dependencyManagement
元素。通过它元素来管理jar包的版本,让子项目中引用一个依赖而不用显示的列出版本号。
结合maven的自定义属性,我们来看看项目中的具体应用:
在父项目中用dependencyManagement
声明依赖的版本,需要
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>io.zipkin.javagroupId>
<artifactId>zipkinartifactId>
<version>${zipkin.version}version>
dependency>
[...]
<dependencies>
dependencyManagement>
在子项目中,Maven会沿着父子层次向上走,直到找到一个拥有dependencyManagement元素的项目,然后它就会使用在这个dependencyManagement元素中指定的版本号。
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
dependency>
<dependency>
<groupId>io.zipkin.javagroupId>
<artifactId>zipkinartifactId>
dependency>
[...]
<dependencies>
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.specification.version |
Java 运行时环境规范版本 |
java.specification.vendor |
Java 运行时环境规范供应商 |
java.specification.name |
Java 运行时环境规范名称 |
java.class.version |
Java 类格式版本号 |
java.class.path |
Java 类路径 |
java.library.path |
加载库时搜索的路径列表 |
java.io.tmpdir |
默认的临时文件路径 |
java.compiler |
要使用的 JIT 编译器的名称 |
java.ext.dirs |
一个或多个扩展目录的路径 |
os.name |
操作系统的名称 |
os.arch |
操作系统的架构 |
os.version |
操作系统的版本 |
file.separator |
文件分隔符(在 UNIX 系统中是“/”) |
path.separator |
路径分隔符(在 UNIX 系统中是“:”) |
line.separator |
行分隔符(在 UNIX 系统中是“/n”) |
user.name |
用户的账户名称 |
user.home |
用户的主目录 |
user.dir |
用户的当前工作目录 |
所有环境变量都可以使用以env.开头的Maven属性引用
${env.JAVA_HOME}
表示JAVA_HOME环境变量的值;
<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">
…
<!– "Project Build" contains more elements than just the BaseBuild set –>
<build>…build>
<profiles>
<profile>
<!– "Profile Build" contains a subset of "Project Build"s elements –>
<build>…build>
profile>
profiles>
project>
build中的主要标签:Resources和Plugins。
用于引用或排除资源文件
<resources>
<resource>
<targetPath>META-INF/plexustargetPath>
<filtering>falsefiltering>
<directory>${basedir}/src/main/plexusdirectory>
<includes>
<include>configuration.xmlinclude>
includes>
<excludes>
<exclude>**/*.propertiesexclude>
excludes>
resource>
resources>
属性 | 说明 |
---|---|
targetPath | 资源的打包路径,该路径相对target/classes目录 |
filtering | 主要用来替换项目中的资源文件(* .xml、* . properties)当中的$ {…}属性值如$ {db.url} 如果filtering=tru 在resources目录中存在资源文件并且 配置了db.url=aaa的话, 在项目编译的时候,就会自动的把pom文件中的${db.url}替换为aaa |
directory | 描述存放资源的目录,该路径相对POM路径 |
includes | 包含的模式列表,例如**/*.xml |
excludes | 排除的模式列表,例如**/*.xml |
为什么我们需要插件?
1.需要某个特殊的 jar包,但是有不能直接通过maven依赖获取,或者说在其他环境的maven仓库内不存在,那么如何将我们所需要的jar包打入我们的生产jar包中。
2.某个jar包内部包含的文件是我们所需要的,或者是我们希望将它提取出来放入指定的位置 ,那么除了复制粘贴,如何通过maven插件实现呢。
maven 通过 plugins
和 pluginmanagement
来管理插件,类似 dependencies
和 dependencyment
但需要注意的是,plugins
和 pluginmanagement
隶属于 build
标签下,而不是最外层的 project
同样pluginmanagement也不会直接引入插件
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-source-pluginartifactId>
<version>2.1version>
<configuration>
<attach>trueattach>
configuration>
<executions>
<execution>
<phase>compilephase>
<goals>
<goal>jargoal>
goals>
execution>
executions>
plugin>
plugins>
pluginManagement>
同样,子项目继承父项目的plugin设置,并可以自由定义
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-source-pluginartifactId>
plugin>
plugins>
什么是Maven仓库?
Maven仓库就是放置所有JAR文件(WAR,ZIP,POM等等)的地方,所有Maven项目可以从同一个Maven仓库中获取自己所需要的依赖JAR,这节省了磁盘资源。此外,由于Maven仓库中所有的JAR都有其自己的坐标,该坐标告诉Maven它的组ID,构件ID,版本,打包方式等等,因此Maven项目可以方便的进行依赖版本管理。你也不在需要提交JAR文件到SCM仓库中,你可以建立一个组织层次的Maven仓库,供所有成员使用。
简言之,Maven仓库能帮助我们管理构件(主要是JAR)。
maven寻找仓库的顺序大致可以理解为:
补充:
1,如果在找寻的过程中,如果发现该仓库有镜像设置,则用镜像的地址代替。
2,如果仓库的id设置成“central
”,则该配置会覆盖maven默认的中央仓库配置。
Maven缺省的本地仓库地址为${user.home}/.m2/repository
。也就是说,一个用户会对应的拥有一个本地仓库。
你也可以自定义本地仓库的位置,修改${user.home}/.m2/settings.xml
:
<localRepository>D:/.m2/repositorylocalRepository>
还可以在运行时指定本地仓库位置:
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中发现如下配置:
<repositories>
<repository>
<id>centralid>
<name>Central Repositoryname>
<url>https://repo.maven.apache.org/maven2url>
<layout>defaultlayout>
<snapshots>
<enabled>falseenabled>
snapshots>
repository>
repositories>
中央仓库的id为central,远程url地址为http://repo.maven.apache.org/maven2,它关闭了snapshot 快照版本构件下载的支持。
前面我们看到超级POM配置了ID为central的远程仓库,我们可以在POM中配置其它的远程仓库。这样做的原因有很多,比如你有一个局域网的远程仓库,使用该仓库能大大提高下载速度,继而提高构建速度,也有可能你依赖的一个jar在central中找不到,它只存在于某个特定的公共仓库,这样你也不得不添加那个远程仓库的配置。
这里我配置一个远程仓库指向中央仓库的中国镜像:
<project>
...
<repositories>
<repository>
<id>maven-net-cnid>
<name>Maven China Mirrorname>
<url>http://maven.net.cn/content/groups/public/url>
<releases>
<enabled>trueenabled>
releases>
<snapshots>
<enabled>falseenabled>
snapshots>
repository>
repositories>
<pluginRepositories>
<pluginRepository>
<id>maven-net-cnid>
<name>Maven China Mirrorname>
<url>http://maven.net.cn/content/groups/public/url>
<releases>
<enabled>trueenabled>
releases>
<snapshots>
<enabled>falseenabled>
snapshots>
pluginRepository>
pluginRepositories>
...
project>
我们先看一下的配置,你可以在它下面添加多个 ,每个都有它唯一的ID,一个描述性的name,以及最重要的,远程仓库的url。
此外,
true 告诉Maven可以从这个仓库下载releases版本的构件
false 告诉Maven不要从这个仓库下载snapshot版本的构件。
禁止从公共仓库下载snapshot构件是推荐的做法,因为这些构件不稳定,且不受你控制,你应该避免使用。当然,如果你想使用局域网内组织内部的仓库,你可以激活snapshot的支持。
文章结束,等待后续更详细的补充······