pom.xml 基础结构
pom.xml(Project Object Model)是 Maven 项目的核心配置文件。所有 Maven 构建命令都读取 pom.xml,根据其中的配置执行编译、测试、打包、发布等任务。理解 pom.xml 的结构是掌握 Maven 的基础。
为什么需要 pom.xml
Maven 项目统一配置
XML
传统项目的问题:
Ant 项目:
- build.xml 定义构建流程
- 手动管理依赖目录
- 不同项目配置差异大
手动管理:
- 项目配置分散:源码目录、输出目录、依赖等
- 团队成员配置可能不一致
- 新项目需要从头配置
Maven 解决方案:
- pom.xml 统一管理所有项目信息
- 标准化的项目结构
- 一个文件包含所有构建配置
pom.xml 包含的内容
| 配置类别 | 包含内容 |
|---|---|
| 项目标识 | groupId、artifactId、version(坐标) |
| 项目信息 | 名称、描述、URL、开发者 |
| 依赖管理 | 项目依赖的第三方库 |
| 构建配置 | 源码目录、输出目录、插件配置 |
| 环境配置 | Profile 多环境配置 |
| 继承关系 | 父 POM、聚合模块 |
POM 最小结构
最简 pom.xml
XML
<?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
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- 项目坐标:唯一标识项目 -->
<groupId>com.example</groupId>
<artifactId>my-app</artifactId>
<version>1.0.0</version>
</project>
必要元素说明
| 元素 | 说明 | 是否必填 | 示例 |
|---|---|---|---|
| modelVersion | POM 模型版本 | 必填 | 固定值 4.0.0 |
| groupId | 组织标识 | 必填 | com.example |
| artifactId | 项目名称 | 必填 | my-app |
| version | 版本号 | 必填 | 1.0.0 |
modelVersion 为什么固定 4.0.0:
XML
modelVersion 表示 POM 文件的格式版本:
- 4.0.0 是当前 Maven 使用的 POM 格式版本
- Maven 3.x 只支持 4.0.0
- 不要改这个值,固定写 4.0.0
如果写成其他版本:
- Maven 无法解析
- 构建失败
一个能运行的最小项目
XML
创建最小 Maven 项目:
步骤1:创建项目目录
mkdir my-app
cd my-app
步骤2:创建 pom.xml(上面的最小结构)
步骤3:创建源码目录
mkdir -p src/main/java/com/example
步骤4:创建 Java 文件
src/main/java/com/example/App.java:
package com.example;
public class App {
public static void main(String[] args) {
System.out.println("Hello Maven!");
}
}
步骤5:构建
mvn compile
步骤6:运行
mvn exec:java -Dexec.mainClass=com.example.App
输出:Hello Maven!
这就是一个完整的 Maven 项目,只需要最小 pom.xml + 标准目录结构。
POM 完整结构
完整 pom.xml 示例
XML
<?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
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- ====================项目坐标 ==================== -->
<groupId>com.example</groupId>
<artifactId>my-web-app</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>war</packaging> <!-- 打包类型 -->
<!-- ==================== 项目信息 ==================== -->
<name>My Web Application</name>
<description>示例 Web 项目</description>
<url>https://example.com/my-web-app</url>
<inceptionYear>2024</inceptionYear>
<!-- 组织信息 -->
<organization>
<name>Example Company</name>
<url>https://example.com</url>
</organization>
<!-- 开发者信息 -->
<developers>
<developer>
<id>developer1</id>
<name>张三</name>
<email>zhangsan@example.com</email>
<organization>Example Company</organization>
</developer>
</developers>
<!-- 许可证 -->
<licenses>
<license>
<name>Apache License 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
</license>
</licenses>
<!-- ==================== 属性定义 ==================== -->
<properties>
<!-- Java 版本 -->
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- 依赖版本 -->
<spring.version>5.3.20</spring.version>
<junit.version>4.13.2</junit.version>
<servlet.version>4.0.1</servlet.version>
</properties>
<!-- ==================== 依赖管理 ==================== -->
<dependencyManagement>
<dependencies>
<!-- 统一管理版本 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>${spring.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- ==================== 依赖声明 ==================== -->
<dependencies>
<!-- Spring Web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<!-- 版本由 BOM 管理 -->
</dependency>
<!-- Servlet API -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servlet.version}</version>
<scope>provided</scope>
</dependency>
<!-- JUnit 测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<!-- ==================== 构建配置 ==================== -->
<build>
<!-- 最终产物名称 -->
<finalName>${project.artifactId}</finalName>
<!-- 资源文件配置 -->
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering> <!-- 启用变量替换 -->
</resource>
</resources>
<!-- 插件配置 -->
<plugins>
<!-- 编译插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
<!-- WAR 打包插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.2</version>
</plugin>
<!-- 测试插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
</plugin>
</plugins>
</build>
<!-- ==================== 多环境 Profile ==================== -->
<profiles>
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<env>dev</env>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<env>prod</env>
</properties>
</profile>
</profiles>
</project>
POM 元素分类详解
| 类别 | 元素 | 说明 | 必填 |
|---|---|---|---|
| 坐标 | groupId | 组织标识 | 必填 |
| artifactId | 项目名称 | 必填 | |
| version | 版本号 | 必填 | |
| packaging | 打包类型 | 选填(默认jar) | |
| 信息 | name | 项目名称(显示名) | 选填 |
| description | 项目描述 | 选填 | |
| url | 项目网址 | 选填 | |
| 属性 | properties | 自定义属性 | 选填 |
| 依赖 | dependencies | 依赖声明 | 选填 |
| dependencyManagement | 依赖版本管理 | 选填 | |
| 构建 | build | 构建配置 | 选填 |
| 环境 | profiles | 多环境配置 | 选填 |
packaging 打包类型
打包类型说明
| packaging | 说明 | 输出文件 | 使用场景 |
|---|---|---|---|
| jar(默认) | Java 库打包 | *.jar | 普通Java 项目、库项目 |
| war | Web应用打包 | *.war | Web 项目 |
| pom | 聚合/父项目 | 无产物 | 多模块父项目 |
| maven-plugin | Maven插件 | *.jar | 开发 Maven 插件 |
| ear | 企业应用 | *.ear | Java EE 应用 |
| rar | 资源适配器 | *.rar | JCA 资源适配器 |
不同打包类型的默认行为
XML
jar 打包(默认):
- package 阶段:生成 jar 文件
- 默认插件绑定:maven-jar-plugin
war 打包:
- package 阶段:生成 war 文件
- 默认插件绑定:maven-war-plugin
- Web 项目目录结构:
src/main/webapp/(Web资源)
pom 打包:
- 不生成产物
- 用于父项目或聚合项目
- modules 元素定义子模块
packaging示例
XML
<!-- Java 库项目 -->
<packaging>jar</packaging>
<!-- Web 项目 -->
<packaging>war</packaging>
<!-- 父项目/聚合项目 -->
<packaging>pom</packaging>
POM 属性(properties)
属性的作用
XML
为什么使用 properties?
问题:版本号分散在各处
<dependency>
<artifactId>spring-core</artifactId>
<version>5.3.20</version> ← 版号写在这里
</dependency>
<dependency>
<artifactId>spring-context</artifactId>
<version>5.3.20</version> ← 又写在这里
</dependency>
升级版本时:
- 需要改多处
- 可能遗漏
- 版本可能不一致
解决:使用 properties
<properties>
<spring.version>5.3.20</spring.version>
</properties>
<dependency>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
升级时:改一处即可
<spring.version>5.3.21</spring.version>
常用属性定义
Bash
<properties>
<!-- 编译相关 -->
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- 依赖版本 -->
<spring.version>5.3.20</spring.version>
<spring-boot.version>2.7.0</spring-boot.version>
<junit.version>4.13.2</junit.version>
<logback.version>1.2.11</logback.version>
<!-- 自定义属性 -->
<env>dev</env>
<db.url>jdbc:mysql://localhost:3306/dev</db.url>
</properties>
内置属性
| 属性 | 说明 | 示例值 |
|---|---|---|
| ${project.groupId} | 项目 groupId | com.example |
| ${project.artifactId} | 项目 artifactId | my-app |
| ${project.version} | 项目版本 | 1.0.0 |
| ${project.basedir} | 项目根目录 | /path/to/project |
| ${project.build.directory} | 构建目录 | target |
| ${project.build.sourceEncoding} | 源码编码 | UTF-8 |
| ${maven.compiler.source} | Java源版本 | 17 |
| ${maven.compiler.target} | Java目标版本 | 17 |
属性引用示例
XML
<build>
<finalName>${project.artifactId}-${project.version}</finalName>
<!-- 最终产物名:my-app-1.0.0.jar -->
</build>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
POM 继承机制
继承层次结构
XML
Maven POM 继承层次:
超级 POM(Maven 内置)
↓ 默认继承
父 POM(可选)
↓显式继承
项目 POM(pom.xml)
↓ 合并
有效 POM(Effective POM)
超级 POM
所有 pom.xml 都隐式继承超级 POM,超级 POM 定义了 Maven 的默认配置:
XML
超级 POM 提供的默认配置:
默认目录结构:
- src/main/java:源码目录
- src/main/resources:资源目录
- src/test/java:测试源码
- src/test/resources:测试资源
- target:输出目录
默认仓库:
- 中央仓库:https://repo.maven.apache.org/maven2
默认插件版本:
- maven-compiler-plugin
- maven-jar-plugin
- maven-surefire-plugin
- maven-clean-plugin
- maven-install-plugin
- maven-deploy-plugin
- maven-resources-plugin
显式继承父 POM
XML
<!-- 继承 Spring Boot 父 POM -->
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<relativePath/> <!-- 从仓库查找,不使用本地路径 -->
</parent>
<!-- 子项目只需声明 artifactId -->
<artifactId>my-app</artifactId>
<!-- groupId、version 继承自父 POM -->
</project>
继承的好处
| 特性 | 说明 |
|---|---|
| groupId 继承 | 子项目可不声明,继承父项目 |
| version 继承 | 子项目可不声明,继承父项目版本 |
| properties 继承 | 父项目定义的属性子项目可用 |
| dependencyManagement 继承 | 父项目管理的依赖版本子项目可用 |
| pluginManagement 继承 | 父项目管理的插件配置子项目可用 |
| 配置合并 | 子项目可覆盖父项目配置 |
父 POM 的 relativePath
XML
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<!-- relativePath 配置 -->
<relativePath/> <!-- 从仓库查找,不使用本地路径 -->
</parent>
relativePath 说明:
| relativePath | 说明 |
|---|---|
| 省略 | 默认查找父目录的 pom.xml |
<relativePath/>(空) | 从仓库查找,不使用本地路径 |
<relativePath>../parent/pom.xml</relativePath> | 指定父 POM路径 |
多模块项目的父 POM
text
<!-- 父项目 pom.xml -->
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>my-project-parent</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging> <!-- 父项目必须是 pom -->
<!-- 定义子模块 -->
<modules>
<module>module-a</module>
<module>module-b</module>
<module>module-c</module>
</modules>
<!-- 统一管理依赖版本 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.20</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
<!-- 子模块 module-a/pom.xml -->
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>my-project-parent</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>module-a</artifactId> <!-- 只需声明 artifactId -->
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<!-- 版本继承 dependencyManagement -->
</dependency>
</dependencies>
</project>
有效 POM(Effective POM)
什么是有效 POM
text
有效 POM = 超级 POM +父 POM + 项目 POM
所有配置合并后的最终配置:
- 包含所有继承的配置
- 包含所有默认配置
- 包含项目显式配置
查看有效 POM
text
mvn help:effective-pom
输出示例:
text
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>my-app</artifactId>
<version>1.0.0</version>
<!-- 下面都是超级 POM 的默认配置 -->
<repositories>
<repository>
<id>central</id>
<url>https://repo.maven.apache.org/maven2</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>central</id>
<url>https://repo.maven.apache.org/maven2</url>
</pluginRepository>
</pluginRepositories>
<build>
<sourceDirectory>src/main/java</sourceDirectory>
<testSourceDirectory>src/test/java</testSourceDirectory>
<outputDirectory>target/classes</outputDirectory>
<testOutputDirectory>target/test-classes</testOutputDirectory>
<directory>target</directory>
<plugins>
<!-- 所有默认插件配置 -->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
...
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
...
</plugin>
<!-- 其他插件 -->
</plugins>
</build>
</project>
有效 POM 的用途
text
用途1:查看最终配置
- 检查默认目录结构
- 检查默认插件版本
- 检查继承的配置
用途2:诊断问题
- 为什么某个插件版本不对?
- 为什么某个配置没生效?
- 查看合并结果
用途3:学习 Maven 默认配置
- 了解 Maven 内置的默认值
- 理解"约定优于配置"
POM 常见配置场景
场景1:Java 版本配置
text
<!-- 方式1:使用 properties -->
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<!-- 方式2:使用编译插件配置 -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
</plugins>
</build>
场景2:资源过滤配置
text
<!-- application.properties -->
app.version=${project.version}
app.name=${project.artifactId}
<!-- pom.xml 启用资源过滤 -->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering> <!-- 启用变量替换 -->
</resource>
</resources>
</build>
构建后 application.properties:
app.version=1.0.0
app.name=my-app
场景3:最终产物名称
text
<build>
<!-- 方式1:使用 artifactId -->
<finalName>${project.artifactId}</finalName>
<!-- 产物:my-app.jar -->
<!-- 方式2:包含版本 -->
<finalName>${project.artifactId}-${project.version}</finalName>
<!-- 产物:my-app-1.0.0.jar -->
<!-- 方式3:自定义名称 -->
<finalName>my-application</finalName>
<!-- 产物:my-application.jar -->
</build>
场景4:多环境配置
text
<profiles>
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<db.url>jdbc:mysql://localhost:3306/dev</db.url>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<db.url>jdbc:mysql://prod-server:3306/prod</db.url>
</properties>
</profile>
</profiles>
<!-- application.properties -->
database.url=${db.url}
构建:
mvn package -Pdev → database.url=localhost
mvn package -Pprod → database.url=prod-server
POM 配置最佳实践
推荐做法
| 做法 | 说明 |
|---|---|
| 使用 properties 统一版本 | 版号集中管理,方便升级 |
| groupId 用反向域名 | 保证全球唯一性 |
| version 用语义化版本 | 主版本.次版本.增量版本 |
| 显式声明编码 | <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
| 复杂依赖用 dependencyManagement | 统一版本,避免冲突 |
| 多模块项目使用父 POM | 继承配置,减少重复 |
不推荐做法
| 做法 | 问题 |
|---|---|
| 版本号分散 | 升级麻烦,容易遗漏 |
| SNAPSHOT 版本用于生产 | 构建结果不稳定 |
| 省略 modelVersion | 必须写 4.0.0 |
| packaging 不写 | 默认 jar,Web项目需显式 war |
常见问题
问题1:pom.xml 格式错误
text
报错:
[ERROR] Failed to execute goal ...
Invalid POM: Parse error
原因:
- XML 格式错误
- 缺少必要元素
- 命名空间声明错误
解决:
1. 检查 XML 格式
2. 确保 modelVersion=4.0.0
3. 检查命名空间声明
问题2:继承父 POM找不到
text
报错:
[ERROR] Non-resolvable parent POM
原因:
- relativePath 配置错误
- 父 POM 版本不存在
解决:
1. 检查父 POM坐标
2. 检查 relativePath
3. 确保父 POM 已发布到仓库
问题3:属性引用不生效
text
问题:${spring.version} 没有替换
原因:
- properties 未定义
- 资源过滤未启用
解决:
1. 检查 properties 定义
2. 启用资源过滤:<filtering>true</filtering>
要点总结
- 最小 POM:modelVersion + groupId + artifactId + version
- modelVersion 固定4.0.0:不要改这个值
- packaging 默认jar:Web 项目需显式 war
- properties 统一版本:方便管理和升级
- 所有 POM 继承超级 POM:内置默认配置
- 显式继承父 POM:复用配置,统一管理
- effective-pom 查看完整配置:包含所有继承和默认配置
- dependencyManagement 统一版本:多模块项目必备
- 资源过滤启用变量替换:${project.version} 等可替换
📝 发现内容有误?点击此处直接编辑