pom.xml 与 Maven
本章定位 :理解 Maven 项目对象模型(POM)的 XML 结构——GAV 坐标、依赖管理、插件配置和 profiles 多环境构建。
定义与作用
pom.xml (Project Object Model)是 Maven 项目的核心配置文件。它用 XML 描述项目的 坐标 (你是谁)、 依赖 (你需要谁)、 构建方式 (怎么编译打包)和 环境配置 (dev/test/prod 的区别)。
Maven 是 Java 世界的事实标准构建工具。pom.xml 的设计体现了"约定优于配置"——你只需要声明"我是谁、我需要什么",Maven 按约定的目录结构和生命周期自动完成编译、测试、打包、部署。
核心原理:Maven 构建生命周期与 POM 继承
图解释 :每个 pom.xml 都隐式继承自 Maven 内置的 Super POM。你声明的父 POM(如 spring-boot-starter-parent)定义了默认的插件版本和配置。项目 POM 在此基础上添加依赖、插件和 profiles。Maven 命令(如 mvn package)触发对应的生命周期阶段。
语法/结构要点
GAV 坐标(项目唯一标识)
| 元素 | 含义 | 示例 |
|---|---|---|
groupId | 组织/公司标识(反向域名) | com.feixiang.edu |
artifactId | 项目名(模块名) | teaching-system |
version | 版本号 | 1.0.0-SNAPSHOT |
依赖管理
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- version 从父 POM 继承 -->
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope> <!-- 仅测试时可用 -->
</dependency>
</dependencies>
依赖范围(scope)
| scope | 编译 | 测试 | 运行时 | 打包到 WAR | 典型场景 |
|---|---|---|---|---|---|
compile | ✓ | ✓ | ✓ | ✓ | 默认,如 Spring Boot |
provided | ✓ | ✓ | ✗ | ✗ | Servlet API(容器提供) |
runtime | ✗ | ✓ | ✓ | ✓ | JDBC 驱动 |
test | ✗ | ✓ | ✗ | ✗ | JUnit, H2 |
完整示例:小崔配置飞翔科技教学系统 POM
场景说明
飞翔科技的后端 小崔 使用 Spring Boot + MyBatis 搭建教学管理系统。需要管理依赖、配置 Java 17 编译、区分 dev 和 prod 环境的数据库连接。
pom.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>
<!-- 父 POM -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
</parent>
<!-- GAV 坐标 -->
<groupId>com.feixiang.edu</groupId>
<artifactId>teaching-system</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<java.version>17</java.version>
<mybatis.version>3.0.3</mybatis.version>
</properties>
<!-- 依赖 -->
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!-- MySQL 驱动(运行时) -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Lombok(编译时) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<!-- 构建插件 -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
<!-- 多环境 profiles -->
<profiles>
<!-- 开发环境 -->
<profile>
<id>dev</id>
<properties>
<db.url>jdbc:mysql://localhost:3306/edu_dev</db.url>
<db.user>root</db.user>
<db.pass>dev123</db.pass>
</properties>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<!-- 生产环境 -->
<profile>
<id>prod</id>
<properties>
<db.url>jdbc:mysql://prod-db:3306/edu</db.url>
<db.user>edu_app</db.user>
<db.pass>${env.DB_PASSWORD}</db.pass>
</properties>
</profile>
</profiles>
</project>
操作结果
mvn clean package:使用默认 dev profile 编译打包mvn clean package -Pprod:使用 prod profile,连接生产数据库配置mvn dependency:tree:查看完整依赖树,定位冲突
小崔的 pom.xml 成功管理了全部依赖(子 POM 不用写版本号),构建出的 JAR 可以直接 java -jar 运行。
易错场景
错误一:依赖版本冲突(依赖传递的菱形问题)
A → B(v1.0) → D(v1.0)
A → C(v2.0) → D(v2.0)
Maven 默认用"最短路径优先"和"先声明优先"解决冲突。用 mvn dependency:tree 诊断,用 <exclusions> 排除不需要的传递依赖。
错误二:provided scope 的测试陷阱
scope=provided 的依赖(如 Servlet API)在 mvn test 时 默认不可用 。如果单元测试需要 Servlet API,改为 compile 或在测试阶段另加一个 test scope 依赖。
面试考点
| 考点 | 参考答案要点 |
|---|---|
| Maven 的 GAV 坐标是什么? | GroupId(组织)、ArtifactId(项目名)、Version(版本)。三者确定一个唯一的构件(artifact) |
| dependency scope 有哪些? | compile(默认)、provided(容器提供、不打包)、runtime(仅运行时)、test(仅测试)、system(本地 JAR,不推荐) |
| Maven 如何解决依赖冲突? | 最短路径优先(路径短的版本胜出);同等路径时先声明优先。用 exclusions 排除不需要的传递依赖,用 dependencyManagement 统一版本 |