1. 什么是POM
POM代表“Project Object Model” 工程对象模型。通过pom.xml,用xml来表现一个Maven工程。在Maven那帮人面前,说一个项目说的是哲学层面的意思,已经超越了单纯的代码文件集。一个项目包含配置文件,开发人员,开发人员扮演的角色,缺陷跟踪系统,组织和证书,项目存放的URL地址,项目的依赖项和所有保证项目运行的事物。对于项目关心的所有事物都是一站式到达,事实上在Maven中,不需要包含任何代码,只需要一个pom.xml文件即可。
2. 快速一览
<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.0</modelVersion>
<!-- [b]The Basics[/b] -->
<groupId>...</groupId>
<artifactId>...</artifactId>
<version>...</version>
<packaging>...</packaging>
<dependencies>...</dependencies>
<parent>...</parent>
<dependencyManagement>...</dependencyManagement>
<modules>...</modules>
<properties>...</properties>
<!-- [b]Build Settings[/b] -->
<build>...</build>
<reporting>...</reporting>
<!-- [b]More Project Information[/b] -->
<name>...</name>
<description>...</description>
<url>...</url>
<inceptionYear>...</inceptionYear>
<licenses>...</licenses>
<organization>...</organization>
<developers>...</developers>
<contributors>...</contributors>
<!-- [b]Environment Settings[/b] -->
<issueManagement>...</issueManagement>
<ciManagement>...</ciManagement>
<mailingLists>...</mailingLists>
<scm>...</scm>
<prerequisites>...</prerequisites>
<repositories>...</repositories>
<pluginRepositories>...</pluginRepositories>
<distributionManagement>...</distributionManagement>
<profiles>...</profiles>
</project>
基础:
POM包含工程需要的所有信息,包括在build过程中使用到的plugins的配置信息。它有效地声明了“who”(谁), "what"(什么)和"where"(在哪里),而构建过程体现了“when”和“how”。看上去POM不能反映它的生命周期,实际上是可以的。举个例子:通过配置maven-antrun-plugin, 可以有效地集成ant任务到POM中去,在这里就像build.xml告诉ant运行后应该做什么,如果意外阻止ant plugin的执行,运行将继续,你会看到比build.xml更强大的地方,不像build.xml需要依赖之前的执行是否成功。
Maven资源定位方式:
上面定义的那些是Maven2 & 3中最基本的单元,其中groupId,artifactId,version是必须的(如果有继承,groupId 和version可能就不会出现在继承的地方,但是它会在被继承的节点存在),他们就像是地址和时间戳,定位到仓库中的指定地方。
groupId:通常在一个组织或者工程中是唯一的,例如:所有Maven的核心artifacts都放在“org.apache.maven”中。当然“.”在groupId中不是必须存在的,比如junit工程(groupId = "junit"),而且包含"."的groupId不是必须与工程的文档结构匹配,但是在使用过程中,当存储到仓库时,group很像操作系统中的java包结构(可以通过文档分隔符替换“.”变成相对路径)例如“org.codehaus.mojo”这个group是放在$M2_REPO/org/codehaus/mojo
artifactId:这个是当前工程发布出去的名字。尽管groupId非常重要,但是同一个group的人们在讨论时候很少提到它(因为他们的groupId都是一样的)。通过groupId和artifactId,我们可以区别不同的工程,可以定位一个工程在仓库中的位置(例如:groupId:org.apache.maven artifactId:maven-project,我们可以在$M2_REPO/org/apache/maven/maven-project找到对应的工程)。
version:区别一个工程的不同版本。
以上三个信息,在Maven生命周期使用到的时候,会告诉Maven我们需要那个组织的哪个工程的哪个版本。
packaging:有了地址结构groupId:artifactId:version,还有一个标准标签可以完善,指定一个完整的地址,这就是项目的artifact的类型标签“packaging”。当没有packaging的时候,Maven会默认当前artifact为jar类型。我们可以指定为“war”,这样我们就可以把工程打包为war。当前常用的packaging有pom, jar, maven-plugin, ejb, war, ear, rar, par 有时候你可能会看到Maven打出groupId:artifactId:packaging:version。
classifier:一些工程会通过groupId:artifactId:packaging:classifier:version去定位资源。
POM中的关系
Maven另外一个很强大的功能就是处理工程之间的关系;包括依赖,继承,聚合(多组件的工程)。依赖管理在复杂零碎的事物中已经有很悠久的传统了,但是对于项目的琐碎事务才刚开始。“Jarmageddon”迅速形成的关系树会变得庞大和复杂,而“Jar Hell”,所依赖的系统版本不同于开发版本,还有给的错误的版本或者类似jar名之间的冲突。通过通用的本地仓库,Maven解决了上述两个问题,链接正确的工程版本还有其它信息。
Dependencies
POM的基石是它的依赖列表,因为很多工程在构建和运行时需要依赖其它事物。如果Maven做的就是帮助我们管理依赖列表,我们就轻松许多。Maven在编译或者其它过程中下载或者连接到需要的被依赖资源,同时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">
...
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.0</version>
<type>jar</type>
<scope>test</scope>
<optional>true</optional>
</dependency>
...
</dependencies>
...
</project>
groupId, artifactId, version:
如果依赖的资源在Maven资源库中找不到,通过下列三种方法可以添加。
[1]通过install plugin安装到本地
mvn install:install-file -Dfile=non-maven-proj.jar -DgroupId=some.group -DartifactId=non-maven-proj -Dversion=1 -Dpackaging=jar
通过上述地址创建一个POM供我们使用。
[2]创建自己本地仓库并且发布到里面。这是一个很好的方法,尤其是公司里面通过intranet来共享和保持项目同步,有一个Maven行为是 deploy:deploy-file 跟install:install-file的行为很像。
[3]添加依赖项的scope到系统,同时定义一个系统路径。这种方式不提倡。
classifier:
用来区别通过同一个POM构建的,但是内容不同的工程。有一些可选项和一些必选项,如果存在了,在artifact后version之前。
用途:如果一个工程需要提供jre1.5的版本同时有需要提供jre1.4的支持,这时第一个artifact就会有jdk15的classifier,第二个artifact就会有jdk14的classifier;
第二个用途是需要添加辅助的artifact到工程的主artifact。如果你看过Maven的中心库,你会发现,sources和javadoc,这两个classifier是用来区别包含源码跟javadoc的jar包的。
type:相当于依赖的包artifact的packaging类型,默认为jar。
scope:它引用即将发生的任务的classpath同时也限制了依赖的传递,有五个不同的scope
- compile:默认值,存在于所有classpath,此外依赖包会被传递到相关项目。
- provided:类似compile,但是表明你希望,运行时由JDK或者运行时容器提供,一般只在编译时或者测试classpath存在,是不可以被传递的。
- runtime:表明这些依赖资源在编译时是不需要的,但是在运行时要用。存在于测试和运行时classpath,在编译classpath中不存在。
- test:表明这些依赖资源只在测试编译和测试运行时可用。
- system:类似于provided,区别是你需要自己提供jar包,artifact是一直可用,但是在仓库中是找不到的。
systemPath:适用于scope是system的情况,否则如果有这个属性,build不会通过。给的是绝对地址,一般可以通过环境变量的方式添加,类似(${java.home}/lib).一般system scope下,maven会直接去找指定的文件,如果没有就会报错提示。
optional:如果这个工程本身就是一个被依赖资源,那么添加optional (依赖的依赖)。假如:工程A和工程B,A在编译时部分代码需要依赖B,但是运行时不需要B;这时工程x添加工程A到自己的依赖列表里面,这样Maven就不需要安装工程B。工程B就可以标记为optional。
这个属性,在最短的时间里告诉其它工程,当你使用这个工程的时候,被标记为optional的资源不是必须的。
Exclusions
明确告诉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">
...
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-embedder</artifactId>
<version>2.0</version>
<exclusions>
<exclusion>
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
</exclusion>
</exclusions>
</dependency>
...
</dependencies>
...
</project>
exclusions: 它包含一个或者多个exclusion,每一个都包含groupId和artifactId,指明这个资源不被包含,区别于optional的是,是否被安装和使用,exclusions主动将自己从依赖树中删除。