自 2010 年 Maven 3 发布以来,Java 构建生态已经发生了翻天覆地的变化。过去十五年间,模块化编程、并行构建、云原生与容器化已成为主流技术实践,JDK 本身也在快速演进。相比之下,Maven 自身的架构显得有些“老态龙钟”。
Maven 4 的出现,正是为了解决这些长期积累的历史包袱。虽然正式发布日期尚未公布,但它目前已经迭代到第五个发布候选版本(RC5),项目成熟度与变更稳定性都已达到相当高的水平。对于开发者而言,现在正是提前了解、评估和准备升级的绝佳时机。
POM 模型升级:从 4.0.0 到 4.1.0
Maven 4 将 POM 的模型版本升级为 4.1.0:
<project
xmlns="http://maven.apache.org/POM/4.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.1.0
http://maven.apache.org/xsd/maven-4.1.0.xsd">
<modelVersion>4.1.0</modelVersion>
</project>
这里有几个关键点:
- 向后兼容:Maven 4 仍然可以构建基于
4.0.0 模型的 POM 文件。
- 新特性生效:只有将 POM 升级到
4.1.0,你才能享受到 Maven 4 带来的全新能力。
- 版本可推导:理论上
modelVersion 标签可以省略,Maven 会从 XML Schema 声明中自动推导。
简单来说,不升级 POM 也能使用 Maven 4,但只有升级后才能充分体验到其性能与功能上的“红利”。
Build POM / Consumer POM 分离:终结“POM 污染”
这是 Maven 4 最核心、也最具颠覆性的变化之一。在 Maven 3 时代,发布到远程仓库的 POM 文件通常混杂了插件配置、构建细节、父 POM 引用和各种属性。当你作为依赖使用者拉取这些 POM 时,被迫解析了大量与自己项目构建无关的“噪音”信息,这被称为“POM 污染”。
Maven 4 通过引入 POM 扁平化(Flattening)机制彻底解决了这个问题。它正式区分了两种 POM:
| 类型 |
用途 |
| Build POM |
供项目自身在构建时使用 |
| Consumer POM |
发布到仓库,提供给其他项目作为依赖时使用 |
Consumer POM 具备以下清爽的特点:
- 不包含插件配置。
- 不包含父 POM 引用。
- 不包含项目声明但未实际使用的依赖。
- 仅保留真实有效的传递性依赖。
- 所有属性都已被解析为具体的值。
启用这一功能非常简单:
mvn clean install -Dmaven.consumer.pom.flatten=true
在 Maven 3 中,实现类似效果需要借助第三方 Flatten Maven Plugin,而在 Maven 4 中,这已成为一项原生能力。此举将使得依赖解析过程变得更快、更干净、结果也更可预测。
新 Artifact Type:显式控制 classpath 与 module path
进入 Java 模块化时代后,Maven 3 的处理方式显得有些模糊。它通常将普通 JAR 放入 classpath,而将包含 module-info.class 的 JAR 自动推断并放入 module path。这种隐式规则在复杂的模块化项目中不够清晰。
Maven 4 引入了新的依赖类型,让开发者可以显式声明依赖的位置:
<type>classpath-jar</type>
<type>module-jar</type>
此外,对于注解处理器(如 Lombok),Maven 4 也提供了更精细的控制,新增了专门的处理器类型:
processor
classpath-processor
modular-processor
以 Lombok 为例,你可以这样配置:
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<type>classpath-processor</type>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
Maven 4 明确区分了 API 的 classpath 与注解处理器的 classpath,这使得构建语义更加清晰,也有利于 IDE 和后端工具链进行更深度的优化。
Modules 改名为 Subprojects:为 Java 9+ 模块系统“让路”
自 Java 9 引入官方模块系统(Java Platform Module System, JPMS)后,“Modules”这个词就产生了歧义,它既指 Maven 的多模块项目结构,也指 Java 的语言级模块。这常常让初学者和工具感到困惑。
Maven 4 决定釜底抽薪,将原来的 modules 改名为 subprojects,并将 modules 标记为废弃(deprecated)。
<subprojects>
<subproject>project-a</subproject>
<subproject>project-b</subproject>
</subprojects>
同时,它还带来了一系列工程实践上的改进:
- Parent 推断:空的
<parent /> 标签可以自动识别父项目。
- 子项目自动发现:无需在父 POM 中显式声明所有子模块。
- 统一构建时间戳:确保多模块项目输出的一致性。
- 安全发布:如果任何一个子项目构建或发布失败,整个项目都不会被发布。
这是一次从语义层面到工程实践层面的双重升级。
树形生命周期:为并行构建正名
Maven 3 的生命周期本质上是线性的,即便在多模块项目中,也很难实现高效的并行构建。Maven 4 引入了 Tree-based Lifecycle(树形生命周期),彻底改变了这一局面:
- 每个子项目可以独立推进自己的生命周期阶段。
- 只要某个模块的依赖项就绪,其构建任务即可立即启动。
- 对于大型的多模块项目,构建速度有望得到显著提升。
启用并行构建的命令如下:
mvn -b concurrent verify
不容忽视的“小”变化:配置能力大幅增强
除了上述重大变革,Maven 4 还在配置细节上做了诸多增强。
1. 支持条件表达式的 Profile
不再局限于基于操作系统或 JDK 版本的基础判断。Maven 4 的 Profile 支持真正的表达式系统,功能更强大。
<condition>
exists('${project.basedir}/src/**/*.xsd')
&& length(${user.name}) > 5
</condition>
2. 统一的 Sources 模型
Maven 3 使用分散的标签定义源码目录:
<sourceDirectory>...</sourceDirectory>
<testSourceDirectory>...</testSourceDirectory>
Maven 4 将其统一为结构化的 <sources> 模型,更适合多目录、多版本源码或模块化项目的配置,且无需借助插件。
<sources>
<source>
<scope>main</scope>
<directory>my-custom-dir/foo</directory>
</source>
<source>
<scope>test</scope>
<directory>my-custom-dir/bar</directory>
</source>
</sources>
官方升级工具:迁移更轻松
面对如此多的变化,升级会不会很麻烦?Maven 社区提供了官方升级工具 mvnup 来帮助你。
mvnup check # 仅生成升级分析报告,不修改文件
mvnup apply # 根据报告自动应用修改
这个工具会智能分析你现有的 POM 文件、插件使用和项目结构,并给出清晰、可执行的升级建议,让从 Maven 3 到 Maven 4 的迁移之路更加平坦。
Maven 4 的这次重构,可以说是对这个经典构建工具的一次“中年大修”。它没有抛弃过去的生态,而是在兼容的基础上,针对现代 Java 开发的需求进行了深度优化。对于开发者而言,了解这些变化并适时规划升级,将有助于提升未来的项目构建效率与体验。
