Maven 学习笔记1: POM文件的介绍
本文节选翻译自apache的Maven入门教程Introduction to the POM
什么是POM
POM:Project Object Model. POM文件是Maven工程的最核心的文件。POM文件的格式是xml文件,它包含了项目的很多信息,Maven使用POM文件提供的配置信息来构建项目。Maven提供了一些默认配置:
含义 | 默认名称 |
build directory | target |
source directory | src/main/java |
test source directory | src/test/java |
用户可以配置的Maven选项:
project dependencies(依赖), the plugins, goals, build profiles等
另外还有 project versions, description, developers, mailing lists也可以被用户指定。
Super POM
Super POM是Maven的默认POM文件。除非用户显式设定,否则所有的POM文件都会继承Super POM文件中的内容。
Super POM 文件内容
1 <project> 2 <modelVersion>4.0.0</modelVersion> 3 4 <repositories> 5 <repository> 6 <id>central</id> 7 <name>Central Repository</name> 8 <url>https://repo.maven.apache.org/maven2</url> 9 <layout>default</layout> 10 <snapshots> 11 <enabled>false</enabled> 12 </snapshots> 13 </repository> 14 </repositories> 15 16 <pluginRepositories> 17 <pluginRepository> 18 <id>central</id> 19 <name>Central Repository</name> 20 <url>https://repo.maven.apache.org/maven2</url> 21 <layout>default</layout> 22 <snapshots> 23 <enabled>false</enabled> 24 </snapshots> 25 <releases> 26 <updatePolicy>never</updatePolicy> 27 </releases> 28 </pluginRepository> 29 </pluginRepositories> 30 31 <build> 32 <directory>${project.basedir}/target</directory> 33 <outputDirectory>${project.build.directory}/classes</outputDirectory> 34 <finalName>${project.artifactId}-${project.version}</finalName> 35 <testOutputDirectory>${project.build.directory}/test-classes</testOutputDirectory> 36 <sourceDirectory>${project.basedir}/src/main/java</sourceDirectory> 37 <scriptSourceDirectory>${project.basedir}/src/main/scripts</scriptSourceDirectory> 38 <testSourceDirectory>${project.basedir}/src/test/java</testSourceDirectory> 39 <resources> 40 <resource> 41 <directory>${project.basedir}/src/main/resources</directory> 42 </resource> 43 </resources> 44 <testResources> 45 <testResource> 46 <directory>${project.basedir}/src/test/resources</directory> 47 </testResource> 48 </testResources> 49 <pluginManagement> 50 <!-- NOTE: These plugins will be removed from future versions of the super POM --> 51 <!-- They are kept for the moment as they are very unlikely to conflict with lifecycle mappings (MNG-4453) --> 52 <plugins> 53 <plugin> 54 <artifactId>maven-antrun-plugin</artifactId> 55 <version>1.3</version> 56 </plugin> 57 <plugin> 58 <artifactId>maven-assembly-plugin</artifactId> 59 <version>2.2-beta-5</version> 60 </plugin> 61 <plugin> 62 <artifactId>maven-dependency-plugin</artifactId> 63 <version>2.8</version> 64 </plugin> 65 <plugin> 66 <artifactId>maven-release-plugin</artifactId> 67 <version>2.5.3</version> 68 </plugin> 69 </plugins> 70 </pluginManagement> 71 </build> 72 73 <reporting> 74 <outputDirectory>${project.build.directory}/site</outputDirectory> 75 </reporting> 76 77 <profiles> 78 <!-- NOTE: The release profile will be removed from future versions of the super POM --> 79 <profile> 80 <id>release-profile</id> 81 82 <activation> 83 <property> 84 <name>performRelease</name> 85 <value>true</value> 86 </property> 87 </activation> 88 89 <build> 90 <plugins> 91 <plugin> 92 <inherited>true</inherited> 93 <artifactId>maven-source-plugin</artifactId> 94 <executions> 95 <execution> 96 <id>attach-sources</id> 97 <goals> 98 <goal>jar-no-fork</goal> 99 </goals> 100 </execution> 101 </executions> 102 </plugin> 103 <plugin> 104 <inherited>true</inherited> 105 <artifactId>maven-javadoc-plugin</artifactId> 106 <executions> 107 <execution> 108 <id>attach-javadocs</id> 109 <goals> 110 <goal>jar</goal> 111 </goals> 112 </execution> 113 </executions> 114 </plugin> 115 <plugin> 116 <inherited>true</inherited> 117 <artifactId>maven-deploy-plugin</artifactId> 118 <configuration> 119 <updateReleaseInfo>true</updateReleaseInfo> 120 </configuration> 121 </plugin> 122 </plugins> 123 </build> 124 </profile> 125 </profiles> 126 127 </project>
Minimal POM
最小POM文件,一个POM文件最少要包含如下信息:
- project root
- modelVersion - 目前应该设置为4.0.0
- groupId - 项目组id
- artifactId - 项目id
- version - 版本号
最小POM文件 example:
1 <project> 2 <modelVersion>4.0.0</modelVersion> 3 <groupId>com.mycompany.app</groupId> 4 <artifactId>my-app</artifactId> 5 <version>1</version> 6 </project>
一个POM文件需要配置其groupId, artifactId和version。 这三个值构成该项目唯一合法的artifact name(或者说 坐标)。其形式是:<groudId>:<artifactId>:<version>。以上面的最小POM文件为例,其合法的fully artifact name是 "com.mycompany.app:my-app:1"。
除了上述最小POM文件要求提供的信息外,其他的配置信息如果不提供,则Maven会采用默认配置。如项目的打包类型信息可以被 packaging type 指定。每个项目都有打包类型,一般可以指定的类型有pom, jar, war等。如果不指定,默认的打包类型是jar。
另外,最小POM文件中并未指定 repositories。如果使用最小POM构建工程,则它会继承Super POM文件中的 repositories 配置。所以当Maven在执行项目遇到一些依赖包的时候,会从 http://repo.maven.apache.org/maven2 这个仓库下载,因为这个仓库被配置在Super POM中。
下面介绍如果使用Maven解决多项目合作的问题。如果一个大的项目下有多个子项目,则这些项目之间可以用继承(inheritance)或者融合(aggregation)的方式进行关联。
Project Inheritance
两个POM文件之间可以存在继承的关系,也就是说,一个POM文件可以复用(或者说两个POM文件之间可以合并)下列信息:
- dependencies
- developers and contributors
- plugin lists (including reports)
- plugin executions with matching ids
- plugin configuration
- resources
Example 1
场景
我们想在一个新项目中继承上面的这个POM, com.mycompany.app:my-app:1.我们现在有如下的一个新的项目 com.mycompany.app:my-module:1.
1 <project> 2 <modelVersion>4.0.0</modelVersion> 3 <groupId>com.mycompany.app</groupId> 4 <artifactId>my-module</artifactId> 5 <version>1</version> 6 </project>
这两个项目的目录结构如下:
1 . 2 |-- my-module 3 | `-- pom.xml 4 `-- pom.xml
其中 my-module/pom.xml 是com.mycompany.app:my-module:1的POM, 外层的pom.xml 是com.mycompany.app:my-app:1的POM
解决方法
如果我们想让com.mycompany.app:my-app:1 变成 com.mycompany.app:my-module:1 的parent artifact, 只需修改com.mycompany.app:my-module:1的POM文件为如下配置:
1 <project> 2 <parent> 3 <groupId>com.mycompany.app</groupId> 4 <artifactId>my-app</artifactId> 5 <version>1</version> 6 </parent> 7 <modelVersion>4.0.0</modelVersion> 8 <groupId>com.mycompany.app</groupId> 9 <artifactId>my-module</artifactId> 10 <version>1</version> 11 </project>
我们在com.mycompany.app:my-module:1 中添加了parent标签域,指定了父项目的坐标三要素 groupId, artifactId, version。这样,my-module项目就可以继承父项目的一些properties.
此外,如果如果在子项目中省略坐标中的groupId 和 version, 则子项目会使用和父项目一样的groupId 和 version。 但子项目必须指定artifactId。
如下配置com.mycompany.app:my-module:1, 省略了子项目的groupId 和 version, 则my-module会复用parent标签中指定的父项目的groupId和version
<project> <parent> <groupId>com.mycompany.app</groupId> <artifactId>my-app</artifactId> <version>1</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>my-module</artifactId> </project>
Example 2
场景
Example 1中的配置方法只适用于父工程已经安装到本地仓库,或者父工程与子工程之间的文件目录结构也是父子目录的关系。
但如下的目录结构如果不指定父工程的路径则会找不到父工程:
1 . 2 |-- my-module 3 | `-- pom.xml 4 `-- parent 5 `-- pom.xml
如果父工程并未被安装,或者父子工程的实际文件目录结构不是父子目录的形式,则应该在parent标签中使用relativePath标签来指定父工程的相对路径
1 <project> 2 <parent> 3 <groupId>com.mycompany.app</groupId> 4 <artifactId>my-app</artifactId> 5 <version>1</version> 6 <relativePath>../parent/pom.xml</relativePath> 7 </parent> 8 <modelVersion>4.0.0</modelVersion> 9 <artifactId>my-module</artifactId> 10 </project>
指定相对路径的方法与linux下的相对路径方法是一致的。
Projection Aggregation
融合(Aggregation)与继承类似,但也有不同。在继承中,我们站在子项目的角度来看待信息复用:我们在子项目POM中给定父项目POM的坐标。而在融合中,我们站在父项目的角度看待问题:我们在父项目的POM中指定一个父项目有哪些子项目(module)。
这样做之后,父项目就能感知它有哪些子项目,当对一个父项目执行Maven命令的时候,这个命令也会被它的所有子项目执行(module)。
要使用aggregation的方法,要求在父项目的POM中指定如下的信息:
- 将父项目的打包类型指定为 "pom"
- 在父项目的POM中指定其所有子项目的文件路径。
Example 3
场景
还使用之前的两个项目举例:
com.mycompany.app:my-app:1's POM
1 <project> 2 <modelVersion>4.0.0</modelVersion> 3 <groupId>com.mycompany.app</groupId> 4 <artifactId>my-app</artifactId> 5 <version>1</version> 6 </project>
com.mycompany.app:my-module:1's POM
1 <project> 2 <modelVersion>4.0.0</modelVersion> 3 <groupId>com.mycompany.app</groupId> 4 <artifactId>my-module</artifactId> 5 <version>1</version> 6 </project>
目录结构
1 . 2 |-- my-module 3 | `-- pom.xml 4 `-- pom.xml
解决方法
如果我们想把my-module子项目aggregate 进 my-app父项目,则只需修改my-app的POM文件如下:
1 <project> 2 <modelVersion>4.0.0</modelVersion> 3 <groupId>com.mycompany.app</groupId> 4 <artifactId>my-app</artifactId> 5 <version>1</version> 6 <packaging>pom</packaging> 7 8 <modules> 9 <module>my-module</module> 10 </modules> 11 </project>
为了进行aggregate,我们在my-app的POM中添加了packaging 标签来指定打包方式为pom, 添加了modules 标签来指定子项目。如果不指定子项目的路径,则默认的相对路径是父项目的路径。(实践中,一个较好的习惯是使用项目的artifact name 作为项目的文件夹名称)
如上配置之后,任何针对父项目 my-app运行的maven命令,同时也会被它的子项目 my-app运行。但也有一些maven命令(goals相关的),会对aggregation 有一些不同的表现。
Example 4
情景
my-app和my-module的配置还是如上,但文件目录改为下面:
1 . 2 |-- my-module 3 | `-- pom.xml 4 `-- parent 5 `-- pom.xml
这时父项目如何指定子项目呢?
解决方案
在module标签中指定子项目的相对路径
1 <project> 2 <modelVersion>4.0.0</modelVersion> 3 <groupId>com.mycompany.app</groupId> 4 <artifactId>my-app</artifactId> 5 <version>1</version> 6 <packaging>pom</packaging> 7 8 <modules> 9 <module>../my-module</module> 10 </modules> 11 </project>
Project Inheritance vs Project Aggregation
继承方式与融合方式的对比
继承方式
如果有多个Maven项目,并且他们有相似的配置,那么我们就可以通过将相同的配置拉取出来作为他们的父项目来结构化这多个Maven项目。让这些项目都继承这个父项目,这样在后面维护这些项目的之后,如果这些相似的配置中有些需要更改,我们只需要修改父项目的配置信息即可。
融合方式
如果我们有一组Maven项目每次都需要共同构建或者处理,则我们可以建立一个父项目,然后在父项目中将这组项目声明为父项目的module。这样我们只需要对父项目运行Maven指令,所有的子项目都会执行相同的指令。
总结
两种方式各有其应用场景,而且这两种方法在项目中可以同时使用。不管是哪种方法,我们需要记住下面三个原则:
- 在子项目中声明谁是它的父项目
- 把父项目的packaging标签的值设为pom
- 在父项目中声明它所有子项目的文件路径。
Example 5
同时使用inheritance 和 aggregation方式
仍然使用上面的my-app和my-module的POM文件:
com.mycompany.app:my-app:1's POM
1 <project> 2 <modelVersion>4.0.0</modelVersion> 3 <groupId>com.mycompany.app</groupId> 4 <artifactId>my-app</artifactId> 5 <version>1</version> 6 </project>
com.mycompany.app:my-module:1's POM
1 <project> 2 <modelVersion>4.0.0</modelVersion> 3 <groupId>com.mycompany.app</groupId> 4 <artifactId>my-module</artifactId> 5 <version>1</version> 6 </project>
directory structure
1 . 2 |-- my-module 3 | `-- pom.xml 4 `-- parent 5 `-- pom.xml
解决方法
应用上面提供的三个原则,修改my-app和my-module的POM文件到如下配置:
com.mycompany.app:my-app:1's POM
1 <project> 2 <modelVersion>4.0.0</modelVersion> 3 <groupId>com.mycompany.app</groupId> 4 <artifactId>my-app</artifactId> 5 <version>1</version> 6 <packaging>pom</packaging> 7 8 <modules> 9 <module>../my-module</module> 10 </modules> 11 </project>
com.mycompany.app:my-module:1's POM
1 <project> 2 <parent> 3 <groupId>com.mycompany.app</groupId> 4 <artifactId>my-app</artifactId> 5 <version>1</version> 6 <relativePath>../parent/pom.xml</relativePath> 7 </parent> 8 <modelVersion>4.0.0</modelVersion> 9 <artifactId>my-module</artifactId> 10 </project>
Project Interpolation and Variables
Maven的设计哲学之一是拒绝重复。在某些场景下,我们需要在POM中多次使用相同的值。为了保证这些值的一致性,Maven支持类似于宏定义的方式定义变量。在POM中使用宏变量,则会被替换为定义位置处的值。此外,有一些Maven预定义的值可以在POM中使用。
如,如果要使用 project.version 变量,可以这么做:
<version>${project.version}</version>
需要注意的是,Maven是先处理继承,后处理变量。因此如果在父项目中使用变量,则这个变量最终会使用子项目中的定义,而不是父项目的定义。
可用的一些变量
Project Model Variables
任何在POM中只具有单个值的标签域都能被当作变量来访问。
如
${project.groupId}
, ${project.version}
, ${project.build.sourceDirectory}等
这些变量在访问时需要加上 project. 前缀。有的变量需要加 pom. 前缀,有时候也可以省略前缀 -- 但这两种方法现在都已经被标记为过时而不应该再使用了。
Special Varibles
project.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 |
1 <project> 2 ... 3 <properties> 4 <maven.build.timestamp.format>yyyy-MM-dd'T'HH:mm:ss'Z'</maven.build.timestamp.format> 5 </properties> 6 ... 7 </project>
指定的格式必须符合API 文档 SimpleDateFormat。如果不指定这些值,则Maven使用默认格式。
另外,可以使用properties标签来自定义变量使用:
1 <project> 2 ... 3 <properties> 4 <mavenVersion>2.1</mavenVersion> 5 </properties> 6 <dependencies> 7 <dependency> 8 <groupId>org.apache.maven</groupId> 9 <artifactId>maven-artifact</artifactId> 10 <version>${mavenVersion}</version> 11 </dependency> 12 <dependency> 13 <groupId>org.apache.maven</groupId> 14 <artifactId>maven-project</artifactId> 15 <version>${mavenVersion}</version> 16 </dependency> 17 </dependencies> 18 ... 19 </project>