Maven中的几个重要概念(三):POM

原文:

http://maven.apache.org/guides/introduction/introduction-to-the-pom.html

本文基本上是翻译+一点点理解。


  • 什么是POM

POM(Project Object Model) 是Maven的基础。它是一个XML文件,包含了Maven用来build项目所需要的项目配置的信息。

(译者:在使用ant进行build时,我们需要通过script告诉ant每一步要做什么,同时提供每一步需要的信息。

例如,首先要编译,编译的源代码在哪里,目标代码放哪里等等。

在Maven中我们不需要告诉Maven应该做些什么,仅需要在pom中提供一些必要的信息即可。对于那些在pom中没有提到的信息,Maven就会使用默认值。

例如Maven默认源代码在src/main/java中。因此Maven在最大程度上简化了项目build的过程。)

它包含了可用于大部分项目的默认值。例如,build directory默认使用<target>定义;source directory,默认使用了src/main/java;test source directory默认使用了src/main/test等等。

在Maven1中(Maven的早期版本)POM的名字是project.xml,而在Maven 2中改为pom.xml。

(译者:通常认为Maven从1到2发生了根本性的改变,因此尽管可能你使用的Maven是3.x的版本,仍然会发现很多以m2命名的目录。)

同时,以前在maven.xml中包含的goal,plugin等信息现在也被放到了pom.xml中。当执行一个task或goal时,Maven搜索当前目录下的pom文件,从其中取出需要的配置信息并执行goal。

(译者:plugin和goal的概念非常重要,请参考我的另外一篇介绍Maven中的lifecycle的文章。)

另外一些需要在POM中配置的信息是project dependencies,也就是那些需要被执行的plugin或goal,build profile等等。其他项目相关信息,包括项目版本,项目描述,开发人员列表,邮件列表等等也可以在POM中。

  • Super POM
Super POM是Maven的默认POM。所有的POM都继承了super POM。也就是说,所有的POM中的信息都来自于Super POM,但在子POM中的配置信息会覆盖Super POM中的重复项。

(译者:文中分别写出了Maven2.0.x和2.1.x的super POM的具体内容。这里请参考原文)


  • 最简单的pom.xml

一个最简单的POM文件需要包括的内容如下:

(译者:也就是说,如果想建立一个简单的Maven项目,提供一个如此简单的POM就够了。Maven会自动完成剩下的事情)

  • project root
  • modelVersion - should be set to 4.0.0
  • groupId - the id of the project's group.
  • artifactId - the id of the artifact (project)
  • version - the version of the artifact under the specified group
具体的例子:

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1</version>
</project>

一个POM必须配置项目的groupId, artifactId, 和version。这三个值构成了项目的fully qualified artifact name。那就是<groupId>:<artifactId>:<version>。对于上面的例子,fully qualified artifact name就是“com.mycompany.app:my-app:1”

同时,正如在第一节提到的,如果一些配置信息没有被提供,Maven将会使用默认值。这些默认值之一是packaging type。如果POM中没有配置它,将会使用默认值jar。

此外,如同你在最简单的POM中看到的,这个POM并没有配置所使用的repositories

(译者:repository是Maven中的另外一个重要概念,不清楚的同学请参考我的另外一篇关于Maven中repository的文章。)

如果你使用这个POM来build你的项目,它将从Super POM继承repository的配置。因此,当Maven发现需要下载POM中的dependency时,它会到Super POM中配置的默认repository,http://repo1.maven.org/maven2去下载。


  • Project Inheritance 项目继承

(译者:project inheritance 和下面要提到的project aggregation 是理解项目间POM父子关系的关键,必须理解。)

另外一些可包含在POM中的信息是:

  • dependencies
  • developers and contributors
  • plugin lists (including reports)
  • plugin executions with matching ids
  • plugin configuration
  • resources

其实Super POM就是项目继承的的一个例子。然而你同样可以通过在POM中加入<parent>,来引入其他的父POM。

示例一:

在前面的例子中我们已经引入了“com.mycompany.app:my-app:1”这个项目,现在我们再引入另一个项目“com.mycompany.app:my-module:1”并以此为例,讲述如何让my-app成为my-module的父项目。

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.mycompany.app</groupId>
  <artifactId>my-module</artifactId>
  <version>1</version>
</project>

同时让它们的目录结构如下:

.
 |-- my-module
 |   `-- pom.xml
 `-- pom.xml

my-module/pom.xml 指的是my-module这个项目的POM,而pom.xml指my-app的POM。

现在,我们把my-module的POM修改成下面:

<project>
  <parent>
    <groupId>com.mycompany.app</groupId>
    <artifactId>my-app</artifactId>
    <version>1</version>
  </parent>

  <modelVersion>4.0.0</modelVersion>
  <groupId>com.mycompany.app</groupId>
  <artifactId>my-module</artifactId>
  <version>1</version>
</project>

注意现在我们加了一个配置,parent。这个配置允许我们设置哪个项目(artifact)是当前项目的父项目。我们需要设置父POM的fully qualified artifact name。配置好之后,my-module的POM就可以从my-app的POM中继承一些配置信息了。

另外,如果我们希望my-module的groupId和/或version与父项目保持一致,我们甚至可以删除my-module的POM中的groupId和/或version。


示例二:


然而上例是有局限性的。只有当父项目已经安装到local repository中,或者必须是特殊的文件结构(父项目的pom.xml在子项目的pom.xml的父一级目录里)时,上例才工作。

(译者:我工作的就是类似的父子项目。子项目依赖于父项目。每次修改了父项目的代码后,必须运行:

mvn install //将父项目编译并加入到repository中

之后才能顺利的运行子项目。)

如果父项目没有安装到local repository中,同时目录结构如下(父子项目在平行的目录结构中)就会有问题:

.
 |-- my-module
 |   `-- pom.xml
 `-- parent
     `-- pom.xml

为了解决这个目录结构(也可以是其他目录结构)导致的问题,我们必须在parent section中使用<relativePath>。

<project>
  <parent>
    <groupId>com.mycompany.app</groupId>
    <artifactId>my-app</artifactId>
    <version>1</version>
    <relativePath>.../parent/pom.xml</relativePath>
  </parent>
  <modelVersion>4.0.0</modelVersion>
  <artifactId>my-module</artifactId>
</project>

如同<relativePath>的字面意思(相对路径),它是module的pom.xml到父pom.xml的相对路径。


  • Project Aggregation 项目集成

project aggregation 和project inheritance类似。但与project inheritance在my-module中设置父POM不同的是,project aggregation是在父POM中设置my-module。

通过这种方式,父项目在知道它的子模块。同时,如果在父项目上执行Maven命令,父项目和所有的子模块都会执行该命令。

(译者:这就是为什么在Maven的POM中既有父子关系,又有子父关系。)

要使用project aggregation, 必须:

1, 将父POM的<packaging>改为“pom”。

2, 在父POM中设置子模块的路径。


示例三:

再次使用之前的pom示例和目录结构,如下:

com.mycompany.app:my-app:1's POM

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1</version>
</project>

com.mycompany.app:my-module:1's POM

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.mycompany.app</groupId>
  <artifactId>my-module</artifactId>
  <version>1</version>
</project>

目录结构:

.
 |-- my-module
 |   `-- pom.xml
 `-- pom.xml

如果我们想把my-module加到my-app中,可以简单的讲my-app的pom.xml改为如下:

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1</version>
  <packaging>pom</packaging>

  <modules>
    <module>my-module</module>
  </modules>

</project>

在这个修改过的pom中,加入了<packaging>和<modules>。前者的值被设置为“pom”,后者设置为 <module>my-module</module>

<module>的值是从com.mycompany.app:my-app:1的pom到com.mycompany.app:my-module:1的pom的相对路径(在例子中使用module的artifactId,即my-module作为目录名,所以<module>设置的不是module名,而是目录的名字)。

现在,对于my-app运行的任何一个Maven命令(或者说phase或goal),同时也会在my-module上运行。


示例四:

但是如果我们将目录改为:

.
 |-- my-module
 |   `-- pom.xml
 `-- parent
     `-- pom.xml

父pom应该怎么设置呢?

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1</version>
  <packaging>pom</packaging>

  <modules>
    <module>../my-module</module>
  </modules>
</project>

答案是,使用和实例三同样的方法,将路径加到<module>中。

  • Project Inheritance和Project aggregation的使用

如果你有好几个Maven项目,同时它们有着类似的配置信息,你可以重构你的这些项目,通过将这些相同的配置抽取出来,放到一个父项目中去。

这样,你需要做的所有事情就是让你的Maven项目继承父项目,然后这些共同的配置就会生效了。

同时,如果你有一组项目,它们需要在一起build和process,你可以创建一个父项目,同时将这些子项目配置成父项目的modules。通过这种方式,你只需要build父项目而子项目就都会build了。

(译者:例如我工作的项目,多个项目,比如项目A,B,C共享同样的核心代码。采取的方式就是将核心代码抽取出来做成一个父项目称为core,将)这些项目配置为父项目的modules。

可以通过build core项目,就build了所有的modules。

但有时也需要变通一下:当运行比如A项目的daily build时,我们并不需要build所有的项目。这时的做法就是,对于core项目,实际有4个pom文件:

pom.xml, A_pom.xml, B_pom.xml, C_pom.xml。当运行A的daily build时,实际在build环境中使用的是A_pom.xml。同时该pom文件中,core的子模块只有A一个。

另外需要注意的一点是:如何build的问题

例如我前面提到的我工作的项目,如果修改了core中的代码,并想重新build项目A时,有三种方法。

1,在core项目上运行mvn build。这样core, A, B, C4个项目都会被build。

2,如果只想build项目A,就用A_pom.xml覆盖core的pom文件,然后运行mvn build。则会build项目core和A。

3,或者,对core项目运行mvn install(该命令会编译,build并将build出来的jar包安装到repository中去), 之后再在A项目运行mvn build)

但是,当然了,你也可以同时使用项目集成和项目继承。意思是,你可以在子项目的pom中配置parent,同时在父项目中配置modules。你只需要明确下面3个规则:

1, 在每一个子项目pom中都要配置parent。

2,将父项目pom的packaging改为“pom”。

3,在父pom中配置子modules。

(译者:到此项目间的关系已经讲完了,让我们来总结一下:

当我们拿到了几个相关项目时,一定要先搞清楚这些项目间的父子关系。我们不仅要考察每一个项目的pom文件,弄清楚他们之间的集成和继承关系,也需要留意项目的目录结构,以防止有些设置并没有生效。

只有弄清楚了项目的关系,我们才能正确的使用Maven的功能)


(译者:原文中还有一个示例五,是关于将上面3点都融合在一起的pom文件,我觉得没有必要再累述了。)

(另外需要说明的一点,如果打开本地的repository,你会发现每个artifact(通常是jar包)也有自己的pom文件。其实原因很简单:Maven也要管理这些artifact。比如类库A,同时也依赖于类库B,C。

因此repository中的artifact也需要pom文件来说明它们之间的关系)


  • 项目的插入值和变量

Maven鼓励的一种做法是,不要自我重复。然而,有时候还是需要在几个不同的地方使用相同值。为了保证仅在唯一的地方定义值,Maven允许你在pom中使用你自己定义的变量。

例如,为了在pom几个不同的地方使用相同的值,project.version,你可以这样做:

<version>${project.version}</version>

同时,在pom文件中定义:

<project>
  ...
  <properties>
    <version>1.0.4</version>
  </properties>
  ...
</project>

需要注意的是,这些变量是在继承机制生效后赋值的。这意味着,如果一个父项目pom包含变量,这些变量会被子项目继承并可以在子项目中直接使用。


这里有三种可使用的变量:

Project Model Variables

所有在pom中的独立的element都可以作为一个变量。例如${project.groupId},${project.version},${project.build.sourceDirectory} 等等。参考POM reference来得到所有的properties。

所有这些变量使用时都应加上前缀"project."。你也可能看到一些以“pom.”为前缀的情况,但是已经不赞成这么做了。

Special Variables

basedir The directory that the current project resides in.
project.baseUri The directory that the current project resides in, represented as an URI.Since Maven 2.1.0
maven.build.timestamp The timestamp that denotes the start of the build. Since Maven 2.1.0-M1

对于build timestamp的格式问题,可以通过<maven.build.timestamp.format>指定,如下:

<project>
  ...
  <properties>
    <maven.build.timestamp.format>yyyyMMdd-HHmm</maven.build.timestamp.format>
  </properties>
  ...
</project>

日期格式需要遵守的规则可以参考 SimpleDateFormat(java的)的API文档。如果没有设置这个值,则使用上例中的默认值。


最后一种就是前面提到的<properties>,不累述。

 

原文到此结束,但我还想加一点关于POM中的dependency的内容。

dependency描述了当前项目对其他项目的依赖关系。如下例,表示当前项目依赖于log4j artifact(并非父子关系)。

    <dependencies>
       <dependency>
       <groupId>log4j</groupId>
       <artifactId>log4j</artifactId>
       <version>1.2.14</version>
       </dependency>

    </dependencies>

同样我们使用的很多第三方工具,如JUnit等等,都需要使用dependency加入到项目的POM中(当然也可以使用项目继承的方法,从父项目继承)。

在eclipse中使用Maven插件时,可以使用图形化界面添加dependency。过程是:

1,点击添加dependency。

2,输入名字,如JUnit。此时Maven插件会到使用的remote repository中搜索相应的类库。

3,搜索到以后,点击希望添加的artifact,确定。此时POM文件已经被修改了,加入了新增的dependency。

4,点击保存。这时Maven就会开始从remote repository下载artifact。

 

如果没有按照Maven插件,另外一种方法就是直接修改pom.xml,加入dependency部分。保存后Maven即开始下载artifact。

不过这里有一个问题,直接修改pom.xml时,往往不知道要添加的类库的完整名称,即<groupId>:<artifactId>:<version>,进而也不知道该怎么填写<groupId><version>等内容。

这里有一个方法,下面的网站是browse repository的:

https://repository.sonatype.org/index.html#nexus-search;quick~junit

使用方法类似:搜索后,找到想要的类库,然后就能看到对应的 <dependency>的内容。直接将该段xml复制到自己的pom.xml中就可以了。


完毕~~~

关于POM,可以参考http://maven.apache.org/pom.html 得到更详细的解释。


看完了Maven中的几个重要概念一,二和三,相信对Maven已经有比较全面的了解了。

这个时候开始看Maven的完全参考手册就会非常轻松了。


你可能感兴趣的:(maven,properties,Module,JUnit,Build,inheritance)