首页 > 代码库 > Maven学习-项目对象模型

Maven学习-项目对象模型

  POM包含了四类描述和配置:

  • 项目总体信息:它包含了一个项目的名称,项目的URL,发起组织,以及项目的开发者贡献者列表和许可证。
  • 构建设置:在这一部分,我们自定义Maven构建的默认行为。我们可以更改源码和测试代码的位置,可以添加新的插件,可以将插件目标绑定到生命周期,我们还可以自定义站点生成参数。
  • 构建环境:包含了一些能在不同使用环境中激活的profile。例如,在开发过程中你可能会想要将应用部署到一个开发服务器上,而在产品环境中你会需要将应用部署到产品服务器上。构建环境为特定的环境定制了构建设置,通常它还会由~/.m2中的自定义settings.xml补充。
  • POM关系:一个项目很少孤立存在;它会依赖于其它项目,可能从父项目继承POM设置,它要定义自身的坐标,可能还会包含子模块

超级POM

  所有的Maven项目的POM都扩展自超级POM,超级POM定义了一组被所有项目共享的默认设置,它是Maven安装的一部分。超级POM中定义了一个远程Maven仓库,该设置可以通过setting.xml来覆盖。超级POM默认关闭了从中央仓量下载snapshop构建的功能。Maven开始于超级POM,然后使用一个或多个父POM覆盖默认配置,最后使用当前项目的POM来覆盖之前生成的配置结果。最后你得到了一个混合了各个POM配置的有效POM。如果你想要查看项目的有效POM,你需要运行Maven Help插件的effective-pom目标。 

mvn help:effective-pom

  执行efftive-pom目标会打印出一个XML文档,该文档是超级POM和各级POM的合并。

  项目版本:主版本、次版本、增量版本和一个限定版本号。

<major version>.<minor version>.<incremental version>-<qualifier>

SNAPSHOT版本

  Maven版本可以包含一个字符串字面量来表示项目正处于活动的开发状态。如果一个版本包含字符串“SNAPSHOT”,Maven就会在安装或发布这个组件的时候将该符号展开为一个日期和时间值,转换为UTC(协调世界时)。例如,如果你的项目有个版本为1.0-SNAPSHOT”并且你将这个项目的构件部署到了一个Maven仓库,如果你在UTC2008年2月7号下午11:08部署了这个版本,Maven就会将这个版本展开成“1.0-20080207-230803-1”。换句话说,当你发布一个snapshot,你没有发布一个软件模块,你只是发布了一个特定时间的快照版本。那么为什么要使用这种方式呢?SNAPSHOT版本在项目活动的开发过程中使用。如果你的项目依赖的一个组件正处于开发过程中,你可以依赖于一个SNAPSHOT版本,在你运行构建的时候Maven会定期的从仓库下载最新的snapshot。作为一个默认设置,Maven不会从远程仓库检查SNAPSHOT版本,要依赖于SNAPSHOT版本,用户必须在POM中使用repository和pluginRepository元素显式的开启下载snapshot功能。

  当发布一个项目的时候,你需要解析所有对SNAPSHOT版本的依赖至正式发布的版本。如果一个项目依赖于SNAPSHOT,那么这个依赖很不稳定,它随时可能变化。发布到非snapshot的Maven仓库(如http://repo1.maven.org/maven2)的构件不能依赖于任何SNAPSHOT版本,因为Maven的超级POM对于中央仓库关闭了snapshot。SNAPSHOT版本只用于开发过程。 

LATEST和RELEASE版本
  当你依赖于一个插件或一个依赖,你可以使用特殊的版本值LATEST或者RELEASE。LATEST是指某个特定构件最新的发布版或者快照版(snapshot),最近被部署到某个特定仓库的构件。RELEASE是指仓库中最后的一个非快照版本。总得来说,设计软件去依赖于一个构件的不明确的版本,并不是一个好的实践。如果你处于软件开发过程中,你可能想要使用RELEASE或者LATEST,这么做十分方便,你也不用为每次一个第三方类库新版本的发布而去更新你配置的版本号。但当你发布软件的时候,你总是应该确定你的项目依赖于某个特定的版本,以减少构建的不确定性,免得被其它不受你控制的软件版本影响。如果无论如何你都要使用LATEST和RELEASE,那么要小心使用。

属性引用
  一个POM可以通过一对大括弧和前面一个美元符号来包含对属性的引用。在Maven读取一个POM的时候,它会在载入POM XML的时候替换这些属性的引用。Maven提供了三个隐式的变量,可以用来访问环境变量,POM信息,和Maven Settings。

  • env:env变量 暴露了你操作系统或者shell的环境变量。
  • Project:project变量暴露了POM。你可以使用点标记(.)的路径来引用POM元素的值。例如,在本节中我们使用过groupId和artifactId来设置构建配置中的finalName元素。这个属性引用的语法是:org.sonatype.mavenbook-${project.artifactId}。
  • Settings:settings变量暴露了Maven settings信息。可以使用点标记(.)的路径来引用settings.xml文件中元素的值。

  除了这三个隐式的变量,你还可以引用系统属性,以及任何在Maven POM中和构建profile中自定义的属性组。

项目依赖
  Maven可以管理内部和外部依赖。范围控制那些依赖在那些classpath中可用,哪些依赖包含在一个应用中。

  • compile(编译范围):compile是默认的范围;如果没有提供一个范围,那该依赖的范围就是编译范围。编译范围依赖在所有的classpath中可用,同时它们也会被打包。
  • provided(已提供范围):provided依赖只有在当JDK或者一个容器已提供该依赖之后才使用。例如,如果你开发了一个web应用,你可能在编译classpath中需要可用的Servlet API来编译一个servlet,但是你不会想要在打包好的WAR中包含这个Servlet API;这个Servlet API JAR由你的应用服务器或者servlet容器提供。已提供范围的依赖在编译classpath(不是运行时)可用。它们不是传递性的,也不会被打包。
  • runtime(运行时范围):runtime依赖在运行和测试系统的时候需要,但在编译的时候不需要。比如,你可能在编译的时候只需要JDBC API JAR,而只有在运行的时候才需要JDBC驱动实现。
  • test(测试范围):test范围依赖在一般的编译和运行时都不需要,它们只有在测试编译和测试运行阶段可用。
  • system(系统范围)system范围依赖与provided类似,但是你必须显式的提供一个对于本地系统中JAR文件的路径。这么做是为了允许基于本地对象编译,而这些对象是系统类库的一部分。这样的构件应该是一直可用的,Maven也不会在仓库中去寻找它。

可选依赖

  如果在编译一个项目的时候你需要依赖某个类库,但是你不希望在使用你类库的项目中,这两个依赖库同时作为传递性运行时依赖出现。可使用如下方式声明可选依赖:

技术分享
<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.sonatype.mavenbook</groupId>
    <artifactId>my-project</artifactId>
    <version>1.0.0</version>
    <dependencies>
        <dependency>
        <groupId>net.sf.ehcache</groupId>
        <artifactId>ehcache</artifactId>
        <version>1.4.1</version>
        <optional>true</optional>
    </dependency>
    ...
    </dependencies>   
</project>     
View Code

依赖版本限定
不是必须为依赖声明某个特定的版本,你可以指定一个满足给定依赖的版本界限。(, )不包含量词[, ]包含量词。

技术分享
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>[3.8,4.0)</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>[,3.8.1]</version>ex-de
    <scope>test</scope>
</dependency>
View Code

  在逗号前面或者后面的版本不是必须的,这种空缺意味着正无穷或者负无穷。当声明一个“正常的”版本如JUnit 3.8.2,内部它其实被表述成“允许任何版本,但最好是3.8.2”。意思是当侦测到版本冲突的时候,Maven会使用冲突算法来选择最好的版本。如果你指定[3.8.2],它意味只有3.8.2会被使用,没有其它。如果其它什么地方有一个版本指定了[3.8.1],你会得到一个构建失败报告,告诉你有版本冲突。我指出这一点是要让你知道有这一选项,但要保守的使用它,只有在确实需要的时候才使用。更好的做法是通dependencyManagement来解决冲突。

传递性依赖
  一个传递性依赖就是对于一个依赖的依赖。对于传递性依赖,Maven建立一个依赖图,并且处理一些可能发生的冲突和重叠。范围如何影响传递性依赖如图所示:最顶层一行代表了传递性依赖范围,最左边的一列代表了直接依赖的范围。行与列的交叉就是为某个传递性依赖指定的范围。空格的意思是传递性依赖被忽略。

技术分享
  如果project-a包含一个对于project-b的测试范围依赖,后者包含一个对于project-c的编译范围依赖。project-c将会是project-a的测试范围传递性依赖。

  排除一个传递性依赖

技术分享
<dependency>
    <groupId>org.sonatype.mavenbook</groupId>
    <artifactId>project-a</artifactId>
    <version>1.0</version>
    <exclusions>
        <exclusion>
            <groupId>org.sonatype.mavenbook</groupId>
            <artifactId>project-b</artifactId>
            </exclusion>
    </exclusions>
</dependency>
View Code

  排除并替换一个传递性依赖

技术分享
<dependencies>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate</artifactId>
        <version>3.2.5.ga</version>
        <exclusions>
            <exclusion>
                <groupId>javax.transaction</groupId>
                <artifactId>jta</artifactId>
            </exclusion>
        </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.geronimo.specs</groupId>
            <artifactId>geronimo-jta_1.1_spec</artifactId>
            <version>1.1</version>
        </dependency>
</dependencies>        
View Code

POM最佳实践
  依赖归类:如果你有一组逻辑上归类在一起的依赖。你可以创建一个打包方式为pom项目来将这些依赖归在一起。
  多模块VS继承:继承于一个父项目和被一个多模块项目管理是有区别的。一个父项目是指它把所有的值传给它的子项目。一个多模块项目只是说它管理一组子模块,或者说一组子项目。多模块关系从上层往下定义。当建立一个多模块项目的时候,你告诉一个项目它的构建需要包含指定的模块。多模块构建用来将模块聚集到一个单独的构建中。父子关系是从叶节点往上定义的。父子关系更多的是处理一个特定项目的定义。当你给一个子项目关联一个父项目的时候,你告诉Maven该项目的POM起源于另一个项目。

Maven学习-项目对象模型