maven之pom

                                           

 一.超级pom

      所有的Maven项目的POM都扩展自超级POM。超级POM定义了一组被所有项目共享的默认设置。它是Maven安装的一部分,可以在 /usr/local/maven/lib 中的maven-model-builder-版本号.jar 文件中找到。

如果你看一下这个JAR文件,你会看到在包\org\apache\maven\model\下看到一个名为 pom-4.0.0.xml 的文件

     1.这个超级POM定义了一些由所有项目继承的标准配置变量。对这些变量的简单解释如下:

     ‚2.默认的超级POM定义了一个单独的远程Maven仓库,ID为 central 。这是所有Maven客户端默认配置访问的中央Maven仓库。该配置可以通过一个自定义的 settings.xml 文件来覆盖。注意这个默认的超级POM关闭了从中央Maven仓库下载snapshot构件的功能(snapshots  false)。如果你需要使用一个snapshot仓库,你就要在你的 pom.xml 或者 settings.xml 中自定义仓库设置。

ƒ    3.中央Maven仓库同时也包含Maven插件。默认的插件仓库就是这个中央仓库。Snapshot被关闭了,而且更新策略被设置成了“从不”(releases never),这意味着Maven将永远不会自动更新一个插件,即使新版本的插件发布了。

    4.build 元素设置Maven标准目录布局中那些目录的默认值。

<project>
  <modelVersion>4.0.0</modelVersion>
  <!--远程仓库-->
  <repositories>
    <repository>
      <id>central</id>
      <name>Central Repository</name>
      <url>https://repo.maven.apache.org/maven2</url>
      <layout>default</layout>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
    </repository>
  </repositories>
  <pluginRepositories>
    <pluginRepository>
      <id>central</id>
      <name>Central Repository</name>
      <url>https://repo.maven.apache.org/maven2</url>
      <layout>default</layout>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
      <releases>
        <updatePolicy>never</updatePolicy>
      </releases>
    </pluginRepository>
  </pluginRepositories>

  <build>
    <directory>${project.basedir}/target</directory>
    <outputDirectory>${project.build.directory}/classes</outputDirectory>
    <finalName>${project.artifactId}-${project.version}</finalName>
    <testOutputDirectory>${project.build.directory}/test-classes</testOutputDirectory>
    <sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
    <scriptSourceDirectory>${project.basedir}/src/main/scripts</scriptSourceDirectory>
    <testSourceDirectory>${project.basedir}/src/test/java</testSourceDirectory>
    <resources>
      <resource>
        <directory>${project.basedir}/src/main/resources</directory>
      </resource>
    </resources>
    <testResources>
      <testResource>
        <directory>${project.basedir}/src/test/resources</directory>
      </testResource>
    </testResources>
    <pluginManagement>
      <!-- NOTE: These plugins will be removed from future versions of the super POM -->
      <!-- They are kept for the moment as they are very unlikely to conflict with lifecycle mappings (MNG-4453) -->
      <plugins>
        <plugin>
          <artifactId>maven-antrun-plugin</artifactId>
          <version>1.3</version>
        </plugin>
        <plugin>
          <artifactId>maven-assembly-plugin</artifactId>
          <version>2.2-beta-5</version>
        </plugin>
        <plugin>
          <artifactId>maven-dependency-plugin</artifactId>
          <version>2.8</version>
        </plugin>
        <plugin>
          <artifactId>maven-release-plugin</artifactId>
          <version>2.3.2</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>

  <reporting>
    <outputDirectory>${project.build.directory}/site</outputDirectory>
  </reporting>

  <profiles>
    <!-- NOTE: The release profile will be removed from future versions of the super POM -->
    <profile>
      <id>release-profile</id>

      <activation>
        <property>
          <name>performRelease</name>
          <value>true</value>
        </property>
      </activation>

      <build>
        <plugins>
          <plugin>
            <inherited>true</inherited>
            <artifactId>maven-source-plugin</artifactId>
            <executions>
              <execution>
                <id>attach-sources</id>
                <goals>
                  <goal>jar</goal>
                </goals>
              </execution>
            </executions>
          </plugin>
          <plugin>
            <inherited>true</inherited>
            <artifactId>maven-javadoc-plugin</artifactId>
            <executions>
              <execution>
                <id>attach-javadocs</id>
                <goals>
                  <goal>jar</goal>
                </goals>
              </execution>
            </executions>
          </plugin>
          <plugin>
            <inherited>true</inherited>
            <artifactId>maven-deploy-plugin</artifactId>
            <configuration>
              <updateReleaseInfo>true</updateReleaseInfo>
            </configuration>
          </plugin>
        </plugins>
      </build>
    </profile>
  </profiles>

</project>

 二.最简单的pom

     1.所有的Maven POM都继承自超级POM。如果你只是编写一个简单的项目,从 src/main/java 目录的源码生成一个JAR,想要运行 src/test/java 中的JUnit测试,想要使用mvn site构建一个项目站点,你不需要自定义任何东西。在这种情况下,你所需要的,是一个最简单的POM。这个POM定义了 groupId , artifactId 和 version :这三项是所有项目都需要的坐标。

    

<project>
<modelVersion>4.0.0</modelVersion>
<groupId>org.zcf</groupId>
<artifactId>simplest-project</artifactId>
<version>1</version>
</project>

   对一个简单的项目来说,这样一个简单的POM已经足够了——例如,一个生成单个JAR文件的Java类库。它不需要和任何其它项目关联,没有任何依赖,也缺少基本的信息如名字和URL。如果创建了这个文件,然后创建了一个子目录 src/main/java ,并且放入了一些源代码,运行mvn package将会生成一个名为 target/simple-project-1.jar 的JAR文件。

三.pom语法

   3.1项目版本

       一个Maven项目发布版本号用 version 编码,用来分组和排序发布。Maven中的版本包含了以下部分:主版本,次版本,增量版本,和限定版本号。一个版本中,这些部分对应如下的格式:

   

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

   例如:版本“1.3.5”由一个主版本1,一个次版本3,和一个增量版本5。而一个版本“5”只有主版本5,没有次版本和增量版本。限定版本用来标识里程碑构建:alpha和beta发布,限定版本通过连字符与主版本,次版本或增量版本隔离。例如,版本“1.3-beta-01”有一个主版本1,次版本3,和一个限定版本“beta-01”。当你想要在你的POM中使用版本界限的时候,保持你的版本号与标准一致十分重要。因为Maven会根据版本号格式来对版本进行排序。

    如果你的版本号与格式<主版本>.<次版本>.<增量版本>-<限定版本>相匹配,它就能被正确的比较;“1.2.3”将被评价成是一个比“1.0.2”更新的构件,这种比较基于主版本,次版本,和增量版本的数值。如果你的版本发布号没有符合本节介绍的标准,那么你的版本号只会根据字符串被比较;“1.0.1b”和“1.2.0b”会使用字符串比较。

   3.2版本构建号

   我们还需要对版本号的限定版本进行排序。以版本号“1.2.3-alpha-2”和“1.2.3-alpha-10”为例,这里“alpha-2”对应了第二次alpha构建,而“alpha-10”对应了第十次alpha构建。虽然“alpha-10”应该被认为是比“alpha-2”更新的构建,但Maven排序的结果是“alpha-10”比“alpha-2”更旧,问题的原因就是我们刚才讨论的Maven处理版本号的方式。

   Maven会将限定版本后面的数字认作一个构建版本。换句话说,这里限定版本是“alpha”,而构建版本是2。虽然Maven被设计成将构建版本和限定版本分离,但目前这种解析还是失效的。因此,“alpha-2”和“alpha-10”是使用字符串进行比较的,而根据字母和数字“alpha-10”在“alpha-2”前面。要避开这种限制,你需要对你的限定版本使用一些技巧。如果你使用“alpha-02”和“alpha-10”,这个问题就消除了,一旦Maven能正确的解析版本构建号之后,这种工作方式也还是能用。

   3.3snapshot版本

    Maven版本可以包含一个字符串字面量来表示项目正处于活动的开发状态。如果一个版本包含字符串“SNAPSHOT”,Maven就会在安装或发布这个组件的时候将该符号展开为一个日期和时间值,转换为UTC(协调世界时)。例如,如果你的项目有个版本为“1.0-SNAPSHOT”并且你将这个项目的构件部署到了一个Maven仓库,如果你在UTC2008年2月7号下午11:08部署了这个版本,Maven就会将这个版本展开成“1.0-20080207-230803-1”。换句话说,当你发布一个snapshot,你没有发布一个软件模块,你只是发布了一个特定时间的快照版本。

   那么为什么要使用这种方式呢?SNAPSHOT版本在项目活动的开发过程中使用。如果你的项目依赖的一个组件正处于开发过程中,你可以依赖于一个SNAPSHOT版本,在你运行构建的时候Maven会定期的从仓库下载最新的snapshot。类似的,如果你系统的下一个发布版本是“1.4”你的项目需要拥有一个“1.4-SNAPSHOT”的版本,之后它被正式发布。作为一个默认设置,Maven不会从远程仓库检查SNAPSHOT版本,要依赖于SNAPSHOT版本,用户必须在POM中使用 repository 和 pluginRepository 元素显式的开启下载snapshot的功能。当发布一个项目的时候,你需要解析所有对SNAPSHOT版本的依赖至正式发布的版本。如果一个项目依赖于SNAPSHOT,那么这个依赖很不稳定,它随时可能变化。发布到非snapshot的Maven仓库(如http://repo1.maven.org/maven2)的构件不能依赖于任何SNAPSHOT版本,因为Maven的超级POM对于中央仓库关闭了snapshot。SNAPSHOT版本只用于开发过程。

   3.4 LATEST 和 RELEASE 版本

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

   3.5属性引用

    Maven提供了三个隐式的变量,可以用来访问环境变量,POM信息,和Maven Settings: 

    env:

    env变量,暴露了你操作系统或者shell的环境变量。便 如在Maven POM中一个对${env.PATH}的引用将会被${PATH}环境变量替换,在Windows中为%PATH%.

 project

    project 变量暴露了POM。你可以使用点标记(.)的路径来引用POM元素的值。例如,在本节中我们使用过 groupId 和 artifactId 来设置构建配置中的 finalName 元素。这个属性引用的语法是: org.sonatype.mavenbook-

${project.artifactId} 。

 settings

    settings 变量暴露了Maven settings信息。可以使用点标记(.)的路径来引用 settings.xml 文件中元素的值。例如, ${settings.offline} 会引用 ~/.m2/settings.xml 文件中 offline 元素的值。

四.依赖

   4.1依赖范围

     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 范围依赖 在一般的 编译和运行时都不需要,它们只有在测试编译和测试运行阶段可用。

   4.2依赖管理

    当你在你的超级复杂的企业中采用Maven之后,你有了两百多个相互关联的Maven项目,你开始想知道是否有一个更好的方法来处理依赖版本。如果每一个使用如MySQL数据库驱动依赖的项目都需要独立的列出该依赖的版本,在你需要升级到一个新版本的时候你就会遇到问题。由于这些版本号分散在你的项目树中,你需要手工的编写每一个引用该依赖的 pom.xml ,确保每个地方的版本号都更改了。即使使用了find,xargs,和,awk,你仍然有漏掉一个POM的风险。

   幸运的是,Maven在 dependencyManagement 元素中为你提供了一种方式来统一依赖版本号。你经常会在一个组织或者项目的最顶层的父POM中看到 dependencyManagement 元素。使用 pom.xml 中的 dependencyManagement 元素能让你在子项目中引用一个依赖而不用显式的列出版本号。Maven会沿着父子层次向上走,直到找到一个拥有 dependencyManagement 元素的项目,然后它就会使用在这个 ependencyManagement 元素中指定的版本号。

   例如,如果你有一大组项目使用MySQL Java connector版本5.1.2,你可以在你的多模 块项目的顶层POM中定义如下的 dependencyManagement 元素。

   子pom

<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.sonatype.mavenbook</groupId>
<artifactId>a-parent</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>project-a</artifactId>
...
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
</project>

   父pom.xml

<project>
<modelVersion>4.0.0</modelVersion>
<groupId>org.sonatype.mavenbook</groupId>
<artifactId>a-parent</artifactId>
<version>1.0.0</version>
...
<dependencyManagement>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.2</version>
</dependency>
...
<dependencies>
</dependencyManagement>

   你应该注意到该子项目没有显式的列出 mysql-connector-java 依赖的版本。由于这个依赖在顶层POM的 dependencyManagement 元素中定义了,该版本号就会传播到所有子项目的 mysql-connector-java 依赖中。注意如果子项目定义了一个版本,它将覆盖顶层POM的 dependencyManagement 元素中的版本。那就是:只有在子项目没有直接声明一个版本的时候, dependencyManagement 定义的版本才会被使用。

  顶层POM中的依赖管理与在一个广泛共享的父POM中定义一个依赖是不同的。对初学者来说,所有依赖都会被继承。如果 mysql-connector-java 在顶层父项目中被作为一个依赖列出,这个层次中的所有项目都将引用该依赖。为了不添加一些不必要的依赖,使用 dependencyManagement 能让你统一并集中化依赖版本的管理,而不用添加那些会被所有子项目继承的依赖。换句话说, dependencyManagement 元素和一个环境变量一样,能让你在一个项目下面的任何地方声明一个依赖而不用指定一个版本号

五. 项目关系

    5.1坐标详解

       groupId

            一个 groupId 归类了一组相关的构件。组定义符基本上类似于一个Java包名。例如: groupId org.apache.maven 是所有由Apache Maven项目生成的构件的基本groupId。组定义符在Maven仓库中被翻译成路径,例如,                groupId    org.apache.maven 可以在repo1.maven.org 1 的 /maven2/org/apache/maven 目录下找到。

      artifactId

           artifactId 是项目的主要定义符。当你生成一个构件,这个构件将由 artifactId 命名。当你引用一个项目,你就需要使用 artifactId 来引用它。 artifactId 和 groupId 的组合必须是唯一的。换句话说,你不能有两个不同的项目拥有同样的 artifactId 和 groupId ;在某个特定的 groupId 下, artifactId 也必须是唯一的。

    version 

       详见3.1,3,2,3,3

    5.2多模块项目

       多模块项目是那些包含一系列待构建模块的项目。一个多模块项目的打包类型总是pom,很少生成一个构件。一个模块项目的存在只是为了将很多项目归类在一起,成为一个构建。下图 “多模块项目关系”展示了一个项目层次,它包含了两个打包类型为 pom 的父项目,另外三个项目的打包类型是 jar :     

                                 

     项目的目录结构如下:

top-group/pom.xml
top-group/sub-group/pom.xml
top-group/sub-group/project-a/pom.xml
top-group/sub-group/project-b/pom.xml
top-group/project-c/pom.xml

 
     5.3项目继承

    有些情况你会想要一个项目从父POM中继承一些值。你可能正构建一个大型的系统,你不想一遍又一遍的重复同样的依赖元素。如果你的项目通过parent元素使用继承,你就可以避免这种重复。当一个项目声明一个parent的时候,它从父项目的POM中继承信息。它也可以覆盖父POM中的值,或者添加一些新的值。

   所有的Maven POM从父POM中继承值。如果一个POM没有通过parent元素指定一个直接的父项目,那这个POM就会从超级POM继承值。例 5.2 “项目继承”展示了 project-a 的 parent 元素,它继承了sub-group 项目定义的POM。 当一个项目指定一个父项目的时候,Maven在读取当前项目的POM之前,会使用这个父POM作为起始点。它继承所有东西,包括 groupId 和 version 。你会注意到 project-a 没有指定 groupId 和 version ,它们从 a-parent 继承而来。有了parent元素,一个POM就只需要定义一个 artifactId 。但这不是强制的, project-a 可以有一个不同的 groupId 和 version ,但如果不提供值,Maven就会使用在父POM中指定的值。

    

      Maven假设父POM在本地仓库中可用,或者在当前项目的父目录( ../pom.xml ) 中可用。如果两个位置都不可用,默认行为还可以通过 relativePath 元素被覆盖。例如,一些组织更喜欢一个平坦的项目结构,父项目的 pom.xml 并不在子项目的父目录中。它可能在项目的兄弟目录中。如果你的子项目在目录 ./project-a 中,父项目在名为 ./a-parent 的目录中,你可以使用如下的配置来指定 parent-a 的POM的相对位置。

<parent>
<groupId>org.sonatype.mavenbook</groupId>
<artifactId>a-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../a-parent/pom.xml</relativePath>
</parent>

 六.pom实践

      6.1依赖归类

         如果你有一组逻辑上归类在一起的依赖。你可以创建一个打包方式为pom项目来将这些依赖归在一起。例如,让我们假设你的应用程序使用Hibernate,一种流行的对象关系映射框架。所有使用Hibernate的项目可能同时依赖于Spring Framework和MySQL JDBC驱动。你可以创建一个特殊的POM,它除了声明一组通用依赖之外什么也不做。这样你就不需要在每个使用Hibernate,Spring和MySQL的项目中包含所有这些依赖。你可以创建一个项目叫做 persistence-deps (持久化依赖的简称),然后让每个需要持久化的项目依赖于这个提供便利的项目。

      在一个单独的POM项目中巩固依赖

<project>
<groupId>org.sonatype.mavenbook</groupId>
<artifactId>persistence-deps</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate</artifactId>
<version>${hibernateVersion}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-annotations</artifactId>
<version>${hibernateAnnotationsVersion}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-hibernate3</artifactId>
<version>${springVersion}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysqlVersion}</version>
</dependency>
</dependencies>
<properties>
<mysqlVersion>(5.1,)</mysqlVersion>
<springVersion>(2.0.6,)</springVersion>
<hibernateVersion>3.2.5.ga</hibernateVersion>
<hibernateAnnotationsVersion>3.3.0.ga</hibernateAnnotationsVersion>
</properties>
</project>

    如果你在一个名为 persistence-deps 的目录下创建了这个项目,你需要做的只是创建该 pom.xml 并且运行mvn install。由于打包类型是 pom ,这个POM被安装到你的本地仓库。你现在就可以添加这个项目作为一个依赖,所有该项目的依赖就会被添加到你的项目中。当我们声明一个对于 persistence-deps 项目的依赖的时候,不要忘了指定依赖类 型为pom。

<project>
<description>This is a project requiring JDBC</description>
...
<dependencies>
...
<dependency>
<groupId>org.sonatype.mavenbook</groupId>
<artifactId>persistence-deps</artifactId>
<version>1.0</version>
<type>pom</type>
</dependency>
</dependencies>
</project>

七.多模块 vs. 继承

     多模块只是父项目把多个模块放到一起打包,模块并不继承父类pom的配置

     集成会继承父类pom的配置

    7.1多模块企业级项目

    有一个的公司顶层POM,其 artifactId 值为 sonatype 。有一个名为 big-system 的多模块项目,引用了子模块 server-side 和 client-side 。

 

     big-system 引用了子模块 client-side 和 server-side 。这两个项目都管理了大量运行在服务端或者客户端的代码。让我们看一下 server-side 项目。在 server-side 下面有一个名为 server-lib 的项目和一个名为 web-apps 的多模块项目。在 web-apps 下面有两个Java web应用: client-web 和 admin-web 。

   让我们从 client-web 和 admin-web 到 web-apps 开始讨论父子关系。由于这两个web应用都用同样的web应用框架实现(假设是Wicket),两个项目都共享同样的一组核心依赖。对Servlet API,JSP API,和Wicket的依赖可以放到 web-apps 项目中。 client-web 和 admin-web 都需要依赖 server-lib ,它就可以定义为一个 web-apps 和 server-lib 之间的依赖。因为 client-web 和 admin-web 通过继承 web-apps 共享了如此多的配置,它们的POM很小,只包含定义符,父项目声明和最终构建名称。

猜你喜欢

转载自zcf9916.iteye.com/blog/2326464