1. 概述
Maven Polyglot 是一组 Maven 核心扩展(core extensions),它允许你使用任意语言来编写 POM 模型,而不再局限于 XML。这包括 JSON、YAML、Groovy、Kotlin 等多种脚本或标记语言。
Maven Polyglot 的核心目标是摆脱 XML 的束缚,因为如今 XML 已经不再是首选语言。
在本文中,我们首先会介绍 Maven 核心扩展的机制和 Maven Polyglot 项目的基本概念。
接着,我们会演示如何编写一个 Maven 核心扩展,让 POM 模型可以从 JSON 文件中构建,而不是传统的 pom.xml。
2. Maven 核心扩展加载机制
Maven 核心扩展是在 Maven 初始化阶段加载的插件,早于项目构建过程。这些扩展允许我们在不修改 Maven 核心代码的前提下,改变其行为。
例如,一个启动时加载的插件可以覆盖 Maven 的默认行为,让它从 pom.xml 以外的文件中读取 POM 模型。
从技术上讲,Maven 核心扩展是一个在 extensions.xml 文件中声明的 Maven artifact:
${projectDirectory}/.mvn/extensions.xml
下面是一个扩展配置的示例:
<?xml version="1.0" encoding="UTF-8"?>
<extensions>
<extension>
<groupId>com.baeldung.maven.polyglot</groupId>
<artifactId>maven-polyglot-json-extension</artifactId>
<version>1.0-SNAPSHOT</version>
</extension>
</extensions>
⚠️ 注意:该机制要求 Maven 版本在 3.3.1 或以上。
3. Maven Polyglot 简介
Maven Polyglot 是一组核心扩展的集合,每个扩展负责从特定的脚本或标记语言中读取 POM 模型。
Polyglot 支持的语言及对应 artifact 如下:
+-----------+-------------------+--------------------------------------+
| Language | Artifact Id | Accepted POM files |
+-----------+-------------------+--------------------------------------+
| Atom | polyglot-atom | pom.atom |
| Clojure | polyglot-clojure | pom.clj |
| Groovy | polyglot-groovy | pom.groovy, pom.gy |
| Java | polyglot-java | pom.java |
| Kotlin | polyglot-kotlin | pom.kts |
| Ruby | polyglot-ruby | pom.rb, Mavenfile, Jarfile, Gemfile |
| Scala | polyglot-scala | pom.scala |
| XML | polyglot-xml | pom.xml |
| YAML | polyglot-yaml | pom.yaml, pom.yml |
+-----------+-------------------+--------------------------------------+
在接下来的章节中,我们会先展示如何使用上述语言之一构建 Maven 项目。
然后,我们将编写一个自定义扩展,支持 JSON 格式的 POM。
4. 使用 Maven Polyglot 扩展
一种方式是使用 Polyglot 项目提供的 artifact,构建一个非 XML 格式的 Maven 项目。
在本例中,我们使用 pom.yaml 作为配置文件创建一个 Maven 项目。
第一步是创建 Maven 核心扩展文件:
${projectDirectory}/.mvn/extensions.xml
然后添加如下内容:
<?xml version="1.0" encoding="UTF-8"?>
<extensions>
<extension>
<groupId>io.takari.polyglot</groupId>
<artifactId>polyglot-yaml</artifactId>
<version>0.3.1</version>
</extension>
</extensions>
你可以根据需要将 artifactId 替换为支持的其他语言,同时检查是否有 更新版本。
最后一步是在 YAML 文件中提供项目元数据:
modelVersion: 4.0.0
groupId: com.baeldung.maven.polyglot
artifactId: maven-polyglot-yml-app
version: 1.0-SNAPSHOT
name: 'YAML Demo'
properties: {maven.compiler.source: 1.8, maven.compiler.target: 1.8}
现在可以像往常一样执行构建命令,例如:
mvn clean install
5. 使用 Polyglot Translate 插件
另一种方式是使用 polyglot-translate-plugin 插件。
这意味着我们可以从现有的 pom.xml 项目出发,通过 translate 插件将其转换为 Polyglot 支持的格式:
mvn io.takari.polyglot:polyglot-translate-plugin:translate -Dinput=pom.xml -Doutput=pom.yml
✅ 这个插件简单粗暴,适合快速迁移项目。
6. 编写自定义扩展
由于 JSON 并不是 Maven Polyglot 默认支持的语言,我们将实现一个简单的扩展,支持从 JSON 文件中读取项目元数据。
我们的扩展会提供一个自定义的 Maven ModelProcessor 实现,用于覆盖 Maven 默认行为。
为此,我们需要改变 POM 文件的定位逻辑,以及如何读取并转换元数据为 Maven 的 Model API。
6.1. Maven 依赖
我们首先创建一个 Maven 项目,加入如下依赖:
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
<version>3.5.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.0</version>
</dependency>
✅ maven-core 是必须的,因为我们要实现核心扩展。
✅ Jackson 用于反序列化 JSON 文件。
由于 Maven 使用 Plexus 依赖注入容器,我们的实现必须是一个 Plexus 组件。所以需要配置如下插件生成 Plexus 元数据:
<plugin>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-component-metadata</artifactId>
<version>1.7.1</version>
<executions>
<execution>
<goals>
<goal>generate-metadata</goal>
</goals>
</execution>
</executions>
</plugin>
6.2. 自定义 ModelProcessor 实现
Maven 通过调用 ModelBuilder.build() 方法来构建 POM 模型,而该方法会委托给 *ModelProcessor.read()*。
Maven 提供了一个 DefaultModelProcessor 实现,它默认从项目根目录的 pom.xml 读取模型。
因此,我们将实现一个自定义的 ModelProcessor 来覆盖默认行为,即 POM 文件的定位和读取方式。
我们先创建一个 CustomModelProcessor 实现,并标记为 Plexus 组件:
@Component(role = ModelProcessor.class)
public class CustomModelProcessor implements ModelProcessor {
@Override
public File locatePom(File projectDirectory) {
return null;
}
@Override
public Model read(
InputStream input,
Map<String, ?> options) throws IOException, ModelParseException {
return null;
}
//...
}
✅ @Component 注解会将该实现注册到 Plexus 容器中。当 Maven 需要注入 ModelProcessor 时,容器会使用我们的实现而非默认的 DefaultModelProcessor。
接着,我们实现 locatePom() 方法,返回 Maven 读取项目元数据的文件:
@Override
public File locatePom(File projectDirectory) {
File pomFile = new File(projectDirectory, "pom.json");
if (!pomFile.exists()) {
pomFile = new File(projectDirectory, "pom.xml");
}
return pomFile;
}
然后实现 read() 方法,读取文件并转换为 Maven Model:
@Requirement
private ModelReader modelReader;
@Override
public Model read(InputStream input, Map<String, ?> options)
throws IOException, ModelParseException {
FileModelSource source = getFileFromOptions(options);
try (InputStream is = input) {
//JSON FILE ==> Jackson
if (isJsonFile(source)) {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(input, Model.class);
} else {
// XML FILE ==> DefaultModelReader
return modelReader.read(input, options);
}
}
return model;
}
✅ 如果是 JSON 文件,使用 Jackson 反序列化;否则使用默认的 XML 读取器。
构建扩展:
mvn clean install
6.3. 使用扩展
为了演示扩展的使用,我们以 Spring Boot Web 项目为例。
首先创建 Maven 项目,并删除 pom.xml。
然后在 ${projectDirectory}/.mvn/extensions.xml 中添加我们实现的扩展:
<?xml version="1.0" encoding="UTF-8"?>
<extensions>
<extension>
<groupId>com.baeldung.maven.polyglot</groupId>
<artifactId>maven-polyglot-json-extension</artifactId>
<version>1.0-SNAPSHOT</version>
</extension>
</extensions>
最后创建 pom.json 文件,内容如下:
{
"modelVersion": "4.0.0",
"groupId": "com.baeldung.maven.polyglot",
"artifactId": "maven-polyglot-json-app",
"version": "1.0.1",
"name": "Json Maven Polyglot",
"parent": {
"groupId": "org.springframework.boot",
"artifactId": "spring-boot-starter-parent",
"version": "2.0.5.RELEASE",
"relativePath": null
},
"properties": {
"project.build.sourceEncoding": "UTF-8",
"project.reporting.outputEncoding": "UTF-8",
"maven.compiler.source": "1.8",
"maven.compiler.target": "1.8",
"java.version": "1.8"
},
"dependencies": [
{
"groupId": "org.springframework.boot",
"artifactId": "spring-boot-starter-web"
}
],
"build": {
"plugins": [
{
"groupId": "org.springframework.boot",
"artifactId": "spring-boot-maven-plugin"
}
]
}
}
现在可以运行项目:
mvn spring-boot:run
✅ 简单粗暴,项目照样跑起来!
7. 总结
本文展示了如何通过 Maven Polyglot 项目改变 Maven 的默认行为,使用非 XML 的语言来定义项目结构。
我们借助 Maven 3.3.1 引入的核心扩展机制,轻松实现了这一目标。
所有代码和示例可在 GitHub 上找到:https://github.com/eugenp/tutorials/tree/master/maven-modules/maven-polyglot