POM 详解
POM(Project Object Model,项目对象模型)是 Maven 的核心概念。每个 Maven 项目都有一个 pom.xml 文件,它定义了项目的基本信息、依赖关系、构建配置等内容。本章将深入讲解 POM 的各个方面。
POM 是什么?
POM 是一个 XML 文件,包含了 Maven 构建项目所需的所有配置信息。可以把 POM 理解为项目的"身份证"和"说明书",它告诉 Maven 这个项目是谁、需要什么、如何构建。
POM 采用声明式的方式来描述项目配置。与 Ant 的 build.xml 这种过程式配置不同,pom.xml 只是声明项目需要什么配置,具体的执行顺序由 Maven 的生命周期来控制。这意味着开发者只需要关注"配置什么",而不需要关心"如何执行"。
最简 POM
一个最简单的 POM 只需要包含三个元素:
<?xml version="1.0" encoding="UTF-8"?>
<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
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>my-project</artifactId>
<version>1.0.0</version>
</project>
让我逐个解释这些元素:
- modelVersion:POM 模型版本,目前固定为
4.0.0,这是 Maven 支持的唯一版本 - groupId:组织或公司标识,用于区分不同组织
- artifactId:项目标识,用于区分同一组织下的不同项目
- version:项目版本号
Maven 坐标
Maven 使用坐标来唯一标识一个构件。坐标由三部分组成:groupId、artifactId、version。这三个元素组合起来形成了构件在全球 Maven 仓库中的唯一地址。
groupId(组织标识)
groupId 表示项目所属的组织或公司。命名规则通常采用域名的倒置形式,例如:
org.springframework:Spring 框架org.apache.maven:Apache Mavencom.alibaba:阿里巴巴
在本地仓库中,groupId 中的点号会被转换为目录分隔符。例如 org.springframework 会存储为 org/springframework 目录。
artifactId(项目标识)
artifactId 是项目的名称,用于区分同一个 groupId 下的不同项目。命名通常使用小写字母和连字符,例如:
spring-core:Spring 核心模块spring-webmvc:Spring Web MVC 模块mybatis:MyBatis 框架
version(版本号)
版本号标识项目的具体版本。版本号的命名规范通常遵循语义化版本:
主版本.次版本.增量版本-里程碑版本
例如:
1.0.0
2.1.3-SNAPSHOT
3.0.0-RC1
版本号中的里程碑标识含义:
| 标识 | 说明 |
|---|---|
| SNAPSHOT | 开发中的快照版本,不稳定 |
| ALPHA | 内部测试版本 |
| BETA | 公开测试版本 |
| RC | Release Candidate,候选发布版本 |
| RELEASE 或 GA | 正式发布版本 |
坐标完整表示
完整的 Maven 坐标表示为:
groupId:artifactId:version[:packaging[:classifier]]
例如:
org.springframework:spring-core:5.3.23
org.apache.maven.plugins:maven-compiler-plugin:3.11.0:maven-plugin
org.junit.jupiter:junit-jupiter:5.9.0:jar:sources
packaging(打包类型)
packaging 元素定义项目的打包类型,决定了项目的构建方式和输出文件的类型。
<packaging>jar</packaging>
Maven 支持的打包类型:
| 类型 | 说明 | 输出文件 |
|---|---|---|
| jar(默认) | Java 应用程序 | .jar 文件 |
| war | Web 应用程序 | .war 文件 |
| pom | 父项目或聚合项目 | 无 |
| ear | 企业应用程序 | .ear 文件 |
| maven-plugin | Maven 插件 | .jar 文件 |
不同的打包类型对应不同的生命周期绑定。例如,jar 打包会在 package 阶段绑定 jar:jar 目标,而 war 打包会绑定 war:war 目标。
项目信息元素
除了基本坐标,POM 还可以包含丰富的项目信息:
<project>
<!-- 基本信息 -->
<name>My Project</name>
<description>这是一个示例项目</description>
<url>https://example.com/my-project</url>
<inceptionYear>2024</inceptionYear>
<!-- 许可证 -->
<licenses>
<license>
<name>Apache License 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
<distribution>repo</distribution>
</license>
</licenses>
<!-- 组织信息 -->
<organization>
<name>Example Inc.</name>
<url>https://example.com</url>
</organization>
<!-- 开发者 -->
<developers>
<developer>
<id>dev1</id>
<name>张三</name>
<email>[email protected]</email>
<roles>
<role>architect</role>
<role>developer</role>
</roles>
</developer>
</developers>
<!-- 贡献者 -->
<contributors>
<contributor>
<name>李四</name>
<email>[email protected]</email>
</contributor>
</contributors>
</project>
这些信息主要用于生成项目站点文档,便于团队协作和项目展示。
properties(属性)
属性是 POM 中的变量,可以在其他地方引用。属性的主要作用是避免重复配置和便于统一管理。
属性定义
<properties>
<!-- 自定义属性 -->
<spring.version>5.3.23</spring.version>
<java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- 编译器配置 -->
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
属性引用
使用 ${propertyName} 语法引用属性:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
内置属性
Maven 提供了丰富的内置属性:
| 属性 | 说明 | 示例值 |
|---|---|---|
${project.version} | 项目版本 | 1.0.0 |
${project.basedir} | 项目根目录 | /home/user/my-project |
${project.build.directory} | 构建目录 | target |
${project.build.finalName} | 最终包名 | my-project-1.0.0 |
${project.name} | 项目名称 | My Project |
${project.url} | 项目 URL | https://example.com |
环境变量和系统属性
<!-- 环境变量 -->
<java.home>${env.JAVA_HOME}</java.home>
<user.home>${env.HOME}</user.home>
<!-- 系统属性 -->
<line.separator>${line.separator}</line.separator>
<file.separator>${file.separator}</file.separator>
依赖配置
依赖配置是 POM 最重要的部分之一。详细内容在依赖管理章节已有讲解,这里补充一些关键点。
依赖的完整结构
<dependency>
<!-- 必需元素 -->
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.23</version>
<!-- 可选元素 -->
<type>jar</type> <!-- 依赖类型,默认 jar -->
<scope>compile</scope> <!-- 依赖范围 -->
<optional>false</optional> <!-- 是否可选 -->
<classifier>sources</classifier> <!-- 分类器 -->
<!-- 排除传递依赖 -->
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
classifier 的使用
classifier 用于区分同一构件的不同变体。常见用途包括:
<!-- 源码包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.23</version>
<classifier>sources</classifier>
</dependency>
<!-- Javadoc 包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.23</version>
<classifier>javadoc</classifier>
</dependency>
<!-- 不同 JDK 版本 -->
<dependency>
<groupId>com.example</groupId>
<artifactId>my-lib</artifactId>
<version>1.0.0</version>
<classifier>jdk11</classifier>
</dependency>
版本范围
Maven 支持灵活的版本范围声明:
<!-- 软性要求:推荐使用 1.0,但可被其他版本替代 -->
<version>1.0</version>
<!-- 硬性要求:必须使用 1.0 -->
<version>[1.0]</version>
<!-- 范围要求:1.0 到 2.0 之间(不包含 2.0) -->
<version>[1.0,2.0)</version>
<!-- 最小版本:大于等于 1.5 -->
<version>[1.5,)</version>
<!-- 最大版本:小于等于 1.0 -->
<version>(,1.0]</version>
<!-- 排除特定版本 -->
<version>(,1.1),(1.1,)</version>
实际开发中,建议使用具体版本号而非版本范围,以保证构建的可重复性。
dependencyManagement
dependencyManagement 用于统一管理依赖版本,常用于多模块项目。
版本集中管理
<!-- 父 POM -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>5.3.23</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
</dependencies>
</dependencyManagement>
子模块使用时无需指定版本:
<!-- 子模块 -->
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<!-- 版本由父 POM 管理 -->
</dependency>
</dependencies>
import scope
import scope 用于导入其他 POM 的 dependencyManagement 配置:
<dependencyManagement>
<dependencies>
<!-- 导入 Spring Boot BOM -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.7.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
使用 import scope 时需要注意:
- 只能用于 dependencyManagement 中
- 目标 POM 的 packaging 必须是 pom
- type 必须指定为 pom
- scope 必须是 import
继承
POM 支持继承机制,子项目可以继承父项目的配置。
父 POM
<!-- 父项目 pom.xml -->
<project>
<groupId>com.example</groupId>
<artifactId>parent-project</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging> <!-- 父项目必须是 pom 类型 -->
<!-- 公共配置 -->
<properties>
<java.version>17</java.version>
</properties>
<dependencyManagement>
<!-- 依赖版本管理 -->
</dependencyManagement>
<dependencies>
<!-- 公共依赖 -->
</dependencies>
</project>
子 POM
<!-- 子项目 pom.xml -->
<project>
<parent>
<groupId>com.example</groupId>
<artifactId>parent-project</artifactId>
<version>1.0.0</version>
<relativePath>../parent-project</relativePath>
</parent>
<artifactId>child-project</artifactId>
<!-- 子项目特有的配置 -->
</project>
可继承的元素
以下元素可以被继承:
- groupId、version
- properties
- dependencies、dependencyManagement
- repositories、pluginRepositories
- build(包括 plugins、pluginManagement)
- reporting
- description、url、organization
- licenses、developers、contributors
- scm、issueManagement、ciManagement
不可继承的元素
以下元素不能被继承:
- artifactId(必须唯一)
- name
- prerequisites
- profiles(但激活的 profile 效果会继承)
relativePath
relativePath 指定父 POM 的相对路径:
<parent>
<groupId>com.example</groupId>
<artifactId>parent-project</artifactId>
<version>1.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
如果省略 relativePath,Maven 会按以下顺序查找父 POM:
- 当前目录的父目录
- 本地仓库
- 远程仓库
设置为空字符串可以禁用本地查找:
<relativePath/>
聚合
聚合允许在一个项目中构建多个模块。
聚合项目
<project>
<groupId>com.example</groupId>
<artifactId>parent-project</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<!-- 聚合的子模块 -->
<modules>
<module>common</module>
<module>service</module>
<module>web</module>
</modules>
</project>
继承与聚合的关系
继承和聚合是两个独立的概念,可以单独使用,也可以组合使用:
- 继承:子项目继承父项目的配置
- 聚合:父项目可以一起构建多个子模块
通常,我们会将两者结合使用:父项目既是聚合项目,也是父 POM。
build 配置
build 元素包含项目构建相关的配置。
基础配置
<build>
<!-- 构建输出目录 -->
<directory>${project.basedir}/target</directory>
<!-- 最终包名 -->
<finalName>${project.artifactId}-${project.version}</finalName>
<!-- 源码目录 -->
<sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
<!-- 测试源码目录 -->
<testSourceDirectory>${project.basedir}/src/test/java</testSourceDirectory>
<!-- 资源目录 -->
<resources>
<resource>
<directory>${project.basedir}/src/main/resources</directory>
</resource>
</resources>
</build>
资源过滤
资源过滤允许在资源文件中使用变量,构建时自动替换:
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/*.properties</include>
<include>**/*.yml</include>
</includes>
</resource>
</resources>
</build>
资源文件中使用变量:
# application.properties
app.name=${project.name}
app.version=${project.version}
db.url=${db.url}
插件配置
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>17</source>
<target>17</target>
<encoding>UTF-8</encoding>
</configuration>
<executions>
<execution>
<id>default-compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
pluginManagement
类似于 dependencyManagement,pluginManagement 用于统一管理插件版本:
<!-- 父 POM -->
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
子模块使用时无需指定版本和配置:
<!-- 子模块 -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<!-- 版本和配置由父 POM 管理 -->
</plugin>
</plugins>
</build>
仓库配置
repositories
配置项目的依赖仓库:
<repositories>
<repository>
<id>aliyun</id>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled>
<updatePolicy>daily</updatePolicy>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</snapshots>
</repository>
</repositories>
pluginRepositories
配置插件仓库:
<pluginRepositories>
<pluginRepository>
<id>aliyun-plugin</id>
<name>阿里云插件仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
</pluginRepository>
</pluginRepositories>
distributionManagement
distributionManagement 配置项目的部署目标:
<distributionManagement>
<!-- 发布版本仓库 -->
<repository>
<id>nexus-releases</id>
<name>Nexus Release Repository</name>
<url>http://localhost:8081/repository/maven-releases/</url>
</repository>
<!-- 快照版本仓库 -->
<snapshotRepository>
<id>nexus-snapshots</id>
<name>Nexus Snapshot Repository</name>
<url>http://localhost:8081/repository/maven-snapshots/</url>
</snapshotRepository>
<!-- 项目站点部署 -->
<site>
<id>website</id>
<url>scp://www.example.com/www/docs/project/</url>
</site>
</distributionManagement>
Super POM
所有 POM 都隐式继承自 Super POM。Super POM 定义了 Maven 的默认配置,包括:
- 默认的目录结构
- 默认的仓库配置(中央仓库)
- 默认的插件版本
可以通过以下命令查看 Super POM 与当前 POM 合并后的有效配置:
mvn help:effective-pom
Super POM 的主要默认配置:
<project>
<modelVersion>4.0.0</modelVersion>
<repositories>
<repository>
<id>central</id>
<name>Central Repository</name>
<url>https://repo.maven.apache.org/maven2</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<build>
<directory>${project.basedir}/target</directory>
<outputDirectory>${project.build.directory}/classes</outputDirectory>
<finalName>${project.artifactId}-${project.version}</finalName>
<sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/java</testSourceDirectory>
</build>
</project>
POM 最佳实践
1. 使用 properties 管理版本
将依赖版本集中定义在 properties 中,便于统一管理和升级:
<properties>
<spring.version>5.3.23</spring.version>
<mybatis.version>3.5.11</mybatis.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
2. 合理使用 dependencyManagement
在多模块项目中,使用父 POM 的 dependencyManagement 统一管理依赖版本。
3. 避免重复配置
利用继承机制,将公共配置放在父 POM 中。
4. 显式声明直接使用的依赖
即使某个依赖会通过传递性依赖引入,如果项目直接使用了它,也应该显式声明。
5. 使用 BOM 管理框架依赖
对于 Spring Boot、Jackson 等框架,使用官方 BOM 确保版本兼容性。
6. 保持 POM 整洁
- 使用有意义的 groupId 和 artifactId
- 添加适当的项目描述信息
- 定期清理不再使用的依赖
小结
本章详细讲解了 POM 的各个方面:
- POM 的概念和作用
- Maven 坐标的组成和含义
- 打包类型的分类和用途
- 属性的定义和使用
- 依赖配置的完整结构
- dependencyManagement 的使用
- POM 继承和聚合机制
- build 配置的详细选项
- 仓库和部署配置
- Super POM 的概念
- POM 的最佳实践
理解 POM 是掌握 Maven 的关键,通过合理配置 POM,可以实现项目的规范化管理。