如果你熟悉
package.json,那 pom.xml 其实就是一个"重度加强版"的package.json。
为什么是 pom.xml
前端项目中,package.json 定义了项目名、版本、依赖、脚本。Java 世界中(Maven 体系),这个文件就是 pom.xml。
POM = Project Object Model,项目对象模型。它不只是依赖清单,它还描述了:
- 项目叫什么、谁在维护、什么版本
- 这个项目依赖哪些 jar 包
- 这个项目怎么编译、怎么打包、怎么测试
- 这个项目怎么发布到仓库
一个典型的 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.yourcompany</groupId>
<artifactId>my-app</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<!-- 依赖、构建、插件 ... -->
</project>
下面逐个拆解。
1. 项目坐标:groupId + artifactId + version
对应 package.json 里的 name + version,但分成了三段:
| 字段 | 含义 | 类比 | 示例 |
|---|---|---|---|
| groupId | 组织/公司标识,一般是域名反写 | 类似 npm scope @company/ |
com.yourcompany |
| artifactId | 项目名称 | 类似 name |
my-app |
| version | 版本号 | 完全等同 version |
1.0.0-SNAPSHOT |
三个合在一起,就是 Maven 世界里的 唯一坐标。任何 jar 包都靠这三个字段定位。
SNAPSHOT 是什么?
-
-SNAPSHOT= 快照版本,相当于前端 "开发中、还没发正式版" -
Maven 每次构建时会检查远程仓库,看是否有更新的 SNAPSHOT
-
去掉
-SNAPSHOT就是正式版本,发布后不可修改1.0.0-SNAPSHOT → 开发中,可以随时覆盖
1.0.0 → 正式版本,一旦发布就不能改
packaging:打包方式
| 值 | 含义 | 类比 |
|---|---|---|
jar |
打成 jar 包(默认) | 类似 npm 发布后的包 |
war |
打成 war 包,部署到 Tomcat 等容器 | 类似打包后部署到 Nginx |
pom |
不打包,仅作为父 POM 管理子模块 | 类似 monorepo 的根 package.json |
2. 依赖管理:dependencies
对应 package.json 的 dependencies + devDependencies。
xml
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.18</version>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<!-- 测试依赖,只在测试阶段生效,类似 devDependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.7.18</version>
<scope>test</scope>
</dependency>
</dependencies>
2.1 scope:依赖范围(前端同学最需要理解的)
scope 决定了这个依赖在什么时候、在哪里可用。
| scope | 编译 | 测试 | 运行 | 打包 | 类比前端 |
|---|---|---|---|---|---|
| compile(默认) | ✅ | ✅ | ✅ | ✅ | dependencies |
| provided | ✅ | ✅ | ❌ | ❌ | TypeScript 类型声明(编译时需要,运行时不需要) |
| runtime | ❌ | ✅ | ✅ | ✅ | 只在运行时才需要的 polyfill |
| test | ❌ | ✅ | ❌ | ❌ | devDependencies |
| system | ✅ | ✅ | ✅ | ❌ | 不推荐,类似直接引用本地路径 |
典型例子:
xml
<!-- Servlet API,Tomcat 已经提供了,打包时不需要 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- Lombok,只在编译期生成代码,运行时不需要 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
2.2 传递依赖:Maven 的"依赖的依赖"
前端中,npm install react 会自动安装 react-dom、scheduler 等子依赖。Maven 也一样:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
这一个依赖会自动引入:spring-webmvc、spring-web、tomcat-embed-core、jackson-databind ...... 几十个包。这就是 传递依赖(transitive dependency)。
查看传递依赖:
bash
mvn dependency:tree
输出类似:
csharp
[INFO] com.yourcompany:my-app:jar:1.0.0-SNAPSHOT
[INFO] +- org.springframework.boot:spring-boot-starter-web:jar:2.7.18:compile
[INFO] | +- org.springframework.boot:spring-boot-starter:jar:2.7.18:compile
[INFO] | | +- org.springframework.boot:spring-boot:jar:2.7.18:compile
[INFO] | | +- org.springframework.boot:spring-boot-autoconfigure:jar:2.7.18:compile
[INFO] | | \- org.slf4j:slf4j-api:jar:1.7.36:compile
[INFO] | +- org.springframework.boot:spring-boot-starter-json:jar:2.7.18:compile
[INFO] | | +- com.fasterxml.jackson.core:jackson-databind:jar:2.13.5:compile
[INFO] | | \- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:jar:2.13.5:compile
[INFO] | +- org.springframework.boot:spring-boot-starter-tomcat:jar:2.7.18:compile
[INFO] | | +- org.apache.tomcat.embed:tomcat-embed-core:jar:9.0.71:compile
[INFO] | | \- org.apache.tomcat.embed:tomcat-embed-websocket:jar:9.0.71:compile
[INFO] | \- org.springframework:spring-webmvc:jar:5.3.27:compile
2.3 排除某个传递依赖
类似 npm 中的 overrides,你可以排除某个不想要的传递依赖:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!-- 不用 Tomcat,换成 Undertow -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
3. 统一版本管理:dependencyManagement
对应前端的 "版本锁定"(package-lock.json 的作用类似,但这里是主动声明)。
在多模块项目中,你希望所有子模块用同一个版本的 Spring、同一个版本的 MySQL 驱动,怎么办?父 POM 里用 <dependencyManagement> 统一声明版本。
xml
<!-- 父 pom.xml -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version> <!-- 统一版本 -->
</dependency>
</dependencies>
</dependencyManagement>
子模块引用时不需要写版本号:
xml
<!-- 子模块 pom.xml -->
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<!-- 版本号从父 POM 继承,不用写 -->
</dependency>
</dependencies>
Spring Boot 的 spring-boot-dependencies 就是这么做的:
xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.18</version>
</parent>
引入这个 parent 之后,你加 spring-boot-starter-web 等依赖都不用写版本号,parent 已经帮你管好了。
4. 仓库配置:repositories
对应 .npmrc 里的 registry。告诉 Maven 去哪里下载依赖。
xml
<repositories>
<!-- 阿里云镜像(国内推荐) -->
<repository>
<id>aliyun</id>
<name>Aliyun Maven Repository</name>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
提示 :仓库配置一般放在
~/.m2/settings.xml(全局配置),而不是每个项目的pom.xml里。
5. 构建配置:build
对应 package.json 的 scripts + 构建工具配置。
xml
<build>
<!-- 最终打包出来的文件名 -->
<finalName>my-app</finalName>
<!-- 资源文件配置 -->
<resources>
<resource>
<directory>src/main/resources</directory>
<!-- 打包时对资源文件中的变量进行替换 -->
<filtering>true</filtering>
</resource>
</resources>
<!-- 插件配置 -->
<plugins>
<!-- Maven 编译插件:指定 JDK 版本 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- Spring Boot 打包插件:打成可执行 jar -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
5.1 核心插件速查
| 插件 | 作用 | 类比前端 |
|---|---|---|
maven-compiler-plugin |
编译 Java 代码,指定 JDK 版本 | tsc 编译 TypeScript |
maven-surefire-plugin |
执行单元测试 | jest / vitest |
maven-jar-plugin |
打成 jar 包 | webpack 打包 |
maven-war-plugin |
打成 war 包 | - |
spring-boot-maven-plugin |
打成可执行的 Spring Boot jar | - |
maven-source-plugin |
同时生成源码 jar | 类似发布到 npm 时包含 source map |
6. 多环境配置:profiles
对应前端在不同环境(开发、测试、生产)使用不同配置。
xml
<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>
激活方式:
bash
# 使用生产环境 profile
mvn clean package -P prod
打包时 src/main/resources 中的 application.yml 里的 ${env} 会被替换为 prod。
7. 常用命令速查
| 命令 | 作用 | 类比 npm |
|---|---|---|
mvn clean |
清理 target 目录 | rm -rf dist/ |
mvn compile |
编译源代码 | tsc / npm run build |
mvn test |
运行单元测试 | npm test |
mvn package |
编译 + 测试 + 打包 | npm run build |
mvn install |
打包 + 安装到本地仓库 | npm link |
mvn deploy |
安装 + 发布到远程仓库 | npm publish |
mvn clean install -DskipTests |
跳过测试直接安装 | - |
mvn dependency:tree |
查看依赖树 | npm ls |
mvn spring-boot:run |
直接启动 Spring Boot 应用 | npm start |
8. 完整 pom.xml 示例
一个典型的 Spring Boot 2.x(Java 8)项目的 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>
<!-- 继承 Spring Boot 父 POM -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.18</version>
</parent>
<!-- 项目坐标 -->
<groupId>com.yourcompany</groupId>
<artifactId>my-app</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>My Application</name>
<!-- 属性 -->
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!-- 依赖 -->
<dependencies>
<!-- Web 核心 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 数据库 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</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>
</plugin>
</plugins>
</build>
</project>
9. 前端 → Java 概念对照表
| 前端概念 | Java (Maven) 概念 |
|---|---|
package.json |
pom.xml |
package-lock.json |
mvn dependency:tree + <dependencyManagement> |
node_modules |
本地 Maven 仓库 ~/.m2/repository |
npm install |
mvn install |
npm publish |
mvn deploy |
npm start |
mvn spring-boot:run |
npm test |
mvn test |
npm run build |
mvn package |
dependencies |
<dependencies> + scope=compile |
devDependencies |
<dependencies> + scope=test 或 provided |
| npm registry | <repositories> / ~/.m2/settings.xml |
npx |
mvn xxx:xxx(直接执行插件) |
10. 写在最后
pom.xml 看着比 package.json 啰嗦很多,XML 嘛,天生就长。但理解了核心概念之后,无非就是:
- 项目坐标(groupId + artifactId + version)→ 这个包叫什么
- 依赖(dependencies)→ 这个包需要别人什么
- 版本管理(dependencyManagement)→ 大家统一用什么版本
- 构建(build + plugins)→ 这个包怎么编译、怎么打包
- 环境(profiles)→ 不同环境怎么切换
掌握这五点,日常开发基本不会懵。遇到问题就 mvn dependency:tree,90% 的依赖问题都能定位到。
祝转 Java 之路顺利! 更多请关注我的微信公众号,感谢!
