本文译自:http://maven.apache.org/pom.html
POM关系
Maven的强大之一就是它的工程关系的处理能力,它包括依赖(和依赖传递)、继承和聚集(多模块的工程)。传统的依赖管理让即使是最简单的工程也会变得异常复杂。“Jarmageddon”虽然快速,但会让依赖树变得大而复杂。“JarHell”在一个系统上所依赖的版本并不等同于那些被开发的版本,它既有可能给出错误的版本,也有可能在相似命名的Jar包之间发生冲突。Maven通过一个共同的本地存储库链接到正确的工程,从而解决了上述的问题。
依赖
POM的基础是它的依赖列表。大多数情况下,每个工程都要依赖于其他的资源的正确编译和运行,那么如果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部件就可以了!”回答是:“当然,这是一件好事情!”。这会强制你只依赖于Maven能够管理的依赖。不幸的是,有些时候,工程不能够从核心的Maven库中下载。例如,工程可能要依赖一个需要许可的Jar包,Maven就会阻止把它放到核心库的操作。有三种方法可以处理这种情况。
1. 使用安装插件安装本地的依赖。这是推荐方法中最简单的方法。例如:
mvn install:install-file -Dfile=non-maven-proj.jar -DgroupId=some.group -DartifactId=non-maven-proj -Dversion=1 -Dpackaging=jar
注意:在命令行中依然需要一个地址,并且安装插件使用给定的地址创建一个POM。
2. 创建自己的依赖库并部署它。这是拥有局域网的企业用户所喜欢的方法,这样让网内的每个用户保持同步。有一个叫做deploy:deploy-file的Maven目标,它类似于install:install-file目标(详细信息请阅读插件的目标)。
3. 给系统设置依赖范围,并定义一个systemPath。不推荐使用该方法,但它会引导我们来解释以下元素:
classifier:classifier用来识别来自同一个POM但内容不同的编译部件。它是可选的,且是任意的字符串,如果有这个元素,它的值会被附加到部件名称中,并放到版本号的后面。
例如,一个工程提供了同时支持JRE1.5和JRE1.4的部件,为了便于用户有选择的使用,分别使用jdk15和jdk14来进行区分。
另一种常用的场景是把次要部件绑定到工程的主要部件中,如果查看Maven的核心库,你会发现使用sources和javadoc的分类标识,它们用于指示把工程的源代码和API文档跟打包的类文件一起部署。
type:它用于指定对应的依赖部件的打包类型。它的默认值是jar。它通常代表着该依赖的文件的扩展名,但也有不一致的时候。一个类型能够被映射成一个不同的扩展名和一个分类器。它的值经常会对应着要使用的包,但也不总是如此。例如,jar、ejb-client和test-jar。通过把插件的extensions属性设置为 true,可以定义新的类型,所以给出的类型不是一个完整的列表。
scope:这个元素不仅指明了任务(编译、运行时、测试等)的类路径,而且也限制了依赖的传递性。以下5个范围是有效的:
A.compile---如果没有指定,默认的会使用这个范围。编译依赖在所有的类路径中都是有效的,此外这些依赖也会被广播到从属工程中。
B.provided---这个范围很像compile,但它只是指定在运行时所期望提供的JDK或容器。它只在编译和测试的类路径中有效,并且是不可传递的。
C.runtime---这个范围指定编译时不需要的,但在运行时需要的依赖。它在运行和测试的类路径中,但不在编译的类路径中。
D.test---这个范围指定在常规情况下应用程序不需要使用的依赖,它只是在测试编译和执行时才有效。
E.system---除了要显示的提供所要保函的JAR之外,其他方面与provided类似。这种部件使用可用,并且在资源库中查不到。
systemPath:如果依赖的scope是system时才使用这个元素,否则设置这个元素会导致编译失败。这个路径必须是绝对路径,因此推荐使用一个属性来指定特定机器的路径(更对信息,请看下面的properties的介绍),例如${java.home}/lib。因为它会假设system范围依赖是要优先安装的,所以Maven不会检查工程的资源库,而会检查确保文件的存在。如果没有,Maven会编译失败,并建议你手动的下载和安装相关的依赖。
optional:当一个工程只依赖它自己的时,就可以标记为依赖是可选的。有点疑惑?举例说明:假设工程A所依赖的工程B的部分编译代码在运行时不被使用,那么对于我们的其他所有工程就不需要工程B。因此如果工程X添加了工程A作为它自己的依赖,那么Maven就不需要安装工程B。也就是说,如果=>代表了必须的依赖,-->代表了可选依赖,即使在编译A时,A=>B,那么在编译X时,X=>A-->B。
简而言之,optional只是让其他的工程了解它,为了正常的工作,在使用这个工程时,你不要这种optional的依赖。