第一部分:Maven插件基础概念
1.1 什么是Maven插件
Maven插件是Maven的核心扩展机制,用于执行特定的构建任务。每个插件包含一个或多个目标(goal),可以绑定到Maven的生命周期阶段。
1.2 插件组成结构
- Mojo: Maven Plain Old Java Object,插件的基本执行单元
- Annotations: 用于配置Mojo的注解
- Descriptor: 插件描述文件
第二部分:简单插件开发
2.1 创建插件项目

<!-- pom.xml -->
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>hello-maven-plugin</artifactId>
<version>1.0.0</version>
<packaging>maven-plugin</packaging>
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>3.8.1</version>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>3.6.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>3.6.0</version>
</plugin>
</plugins>
</build>
</project>
2.2 编写第一个Mojo
package com.example;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
/**
* 简单的Hello World插件
*/
@Mojo(name = "sayhello")
public class HelloMojo extends AbstractMojo {
/**
* 消息参数,用户可配置
*/
@Parameter(property = "message", defaultValue = "Hello World!")
private String message;
/**
* 重复次数
*/
@Parameter(property = "repeat", defaultValue = "1")
private int repeat;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
for (int i = 0; i < repeat; i++) {
getLog().info(message);
}
}
}
2.3 安装和测试插件
# 安装插件到本地仓库
mvn clean install
# 在另一个项目中使用插件
mvn com.example:hello-maven-plugin:1.0.0:sayhello
# 带参数执行
mvn com.example:hello-maven-plugin:1.0.0:sayhello -Dmessage="Custom Message" -Drepeat=3

第三部分:中级插件功能
3.1 访问Maven项目信息
package com.example;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
/**
* 访问项目信息的插件
*/
@Mojo(name = "projectinfo")
public class ProjectInfoMojo extends AbstractMojo {
@Parameter(defaultValue = "${project}", readonly = true, required = true)
private MavenProject project;
@Parameter(property = "showDependencies", defaultValue = "false")
private boolean showDependencies;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
getLog().info("Project: " + project.getName());
getLog().info("GroupId: " + project.getGroupId());
getLog().info("ArtifactId: " + project.getArtifactId());
getLog().info("Version: " + project.getVersion());
getLog().info("Packaging: " + project.getPackaging());
if (showDependencies) {
getLog().info("Dependencies:");
project.getDependencies().forEach(dep ->
getLog().info(" " + dep.getGroupId() + ":" + dep.getArtifactId() + ":" + dep.getVersion())
);
}
}
}
3.2 文件操作插件
package com.example;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
* 文件操作插件
*/
@Mojo(name = "fileops")
public class FileOperationsMojo extends AbstractMojo {
@Parameter(property = "sourceDir", defaultValue = "${project.basedir}/src")
private String sourceDir;
@Parameter(property = "targetDir", defaultValue = "${project.build.directory}/backup")
private String targetDir;
@Parameter(property = "backupEnabled", defaultValue = "false")
private boolean backupEnabled;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
try {
Path sourcePath = Paths.get(sourceDir);
Path targetPath = Paths.get(targetDir);
if (!Files.exists(sourcePath)) {
getLog().warn("Source directory does not exist: " + sourceDir);
return;
}
if (backupEnabled) {
getLog().info("Creating backup from " + sourceDir + " to " + targetDir);
copyDirectory(sourcePath, targetPath);
} else {
getLog().info("Backup is disabled. Set backupEnabled=true to enable.");
}
// 统计文件信息
long fileCount = Files.walk(sourcePath)
.filter(Files::isRegularFile)
.count();
getLog().info("Total files in source: " + fileCount);
} catch (IOException e) {
throw new MojoExecutionException("File operation failed", e);
}
}
private void copyDirectory(Path source, Path target) throws IOException {
Files.walk(source).forEach(sourcePath -> {
Path targetPath = target.resolve(source.relativize(sourcePath));
try {
Files.copy(sourcePath, targetPath);
} catch (IOException e) {
getLog().error("Failed to copy: " + sourcePath, e);
}
});
}
}
3.3 配置复杂参数
package com.example;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import java.util.List;
import java.util.Map;
/**
* 复杂参数配置示例
*/
@Mojo(name = "complexconfig")
public class ComplexConfigMojo extends AbstractMojo {
// 列表参数
@Parameter
private List<String> fileExtensions;
// Map参数
@Parameter
private Map<String, String> propertiesMapping;
// 嵌套对象参数
@Parameter
private ReportConfig reportConfig;
public static class ReportConfig {
private String format;
private boolean includeTimestamp;
private List<String> sections;
// getters and setters
public String getFormat() { return format; }
public void setFormat(String format) { this.format = format; }
public boolean isIncludeTimestamp() { return includeTimestamp; }
public void setIncludeTimestamp(boolean includeTimestamp) {
this.includeTimestamp = includeTimestamp;
}
public List<String> getSections() { return sections; }
public void setSections(List<String> sections) { this.sections = sections; }
}
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
getLog().info("File Extensions: " + fileExtensions);
getLog().info("Properties Mapping: " + propertiesMapping);
if (reportConfig != null) {
getLog().info("Report Format: " + reportConfig.getFormat());
getLog().info("Include Timestamp: " + reportConfig.isIncludeTimestamp());
getLog().info("Sections: " + reportConfig.getSections());
}
}
}
对应的pom配置:
<plugin>
<groupId>com.example</groupId>
<artifactId>hello-maven-plugin</artifactId>
<version>1.0.0</version>
<configuration>
<fileExtensions>
<fileExtension>.java</fileExtension>
<fileExtension>.xml</fileExtension>
</fileExtensions>
<propertiesMapping>
<key1>value1</key1>
<key2>value2</key2>
</propertiesMapping>
<reportConfig>
<format>HTML</format>
<includeTimestamp>true</includeTimestamp>
<sections>
<section>summary</section>
<section>details</section>
</sections>
</reportConfig>
</configuration>
</plugin>
第四部分:高级插件开发
4.1 生命周期绑定
package com.example;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.ResolutionScope;
/**
* 绑定到生命周期的插件
*/
@Mojo(
name = "validate-project",
defaultPhase = LifecyclePhase.VALIDATE,
requiresDependencyResolution = ResolutionScope.COMPILE,
threadSafe = true
)
public class ValidateProjectMojo extends AbstractMojo {
@Parameter(defaultValue = "${project}", readonly = true)
private org.apache.maven.project.MavenProject project;
@Parameter(property = "minJavaVersion", defaultValue = "11")
private int minJavaVersion;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
// 验证Java版本
String javaVersion = System.getProperty("java.version");
int majorVersion = getMajorVersion(javaVersion);
if (majorVersion < minJavaVersion) {
throw new MojoFailureException(
"Java version " + javaVersion + " is below minimum required version " + minJavaVersion
);
}
// 验证项目属性
if (project.getVersion().endsWith("-SNAPSHOT")) {
getLog().warn("Project is using SNAPSHOT version");
}
getLog().info("Project validation completed successfully");
}
private int getMajorVersion(String version) {
if (version.startsWith("1.")) {
return Integer.parseInt(version.substring(2, 3));
} else {
int dotIndex = version.indexOf(".");
if (dotIndex > -1) {
return Integer.parseInt(version.substring(0, dotIndex));
}
return Integer.parseInt(version);
}
}
}
4.2 依赖注入和组件使用
package com.example;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.project.MavenProject;
import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.artifact.resolver.ArtifactResolver;
import org.codehaus.plexus.components.interactivity.Prompter;
import org.codehaus.plexus.components.interactivity.PrompterException;
import java.util.List;
/**
* 使用Maven组件的插件
*/
@Mojo(name = "interactive")
public class InteractiveMojo extends AbstractMojo {
@Parameter(defaultValue = "${project}", readonly = true)
private MavenProject project;
@Component
private ArtifactFactory artifactFactory;
@Component
private ArtifactResolver artifactResolver;
@Component
private Prompter prompter;
@Parameter(property = "interactive", defaultValue = "true")
private boolean interactive;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
try {
if (interactive) {
String action = prompter.prompt("Choose action (validate/analyze/report): ");
switch (action.toLowerCase()) {
case "validate":
performValidation();
break;
case "analyze":
performAnalysis();
break;
case "report":
generateReport();
break;
default:
getLog().warn("Unknown action: " + action);
}
} else {
// 非交互模式执行所有操作
performValidation();
performAnalysis();
generateReport();
}
} catch (PrompterException e) {
throw new MojoExecutionException("Interaction failed", e);
}
}
private void performValidation() {
getLog().info("Performing project validation...");
// 验证逻辑
}
private void performAnalysis() {
getLog().info("Analyzing dependencies...");
// 分析逻辑
}
private void generateReport() {
getLog().info("Generating report...");
// 报告生成逻辑
}
}
4.3 多线程插件
package com.example;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
/**
* 多线程处理插件
*/
@Mojo(name = "parallel", threadSafe = true)
public class ParallelProcessingMojo extends AbstractMojo {
@Parameter(property = "threadCount", defaultValue = "4")
private int threadCount;
@Parameter(property = "tasks")
private List<String> tasks;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
if (tasks == null || tasks.isEmpty()) {
getLog().info("No tasks specified");
return;
}
ExecutorService executor = Executors.newFixedThreadPool(threadCount);
try {
List<CompletableFuture<Void>> futures = tasks.stream()
.map(task -> CompletableFuture.runAsync(() -> processTask(task), executor))
.collect(Collectors.toList());
// 等待所有任务完成
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
getLog().info("All tasks completed successfully");
} finally {
executor.shutdown();
}
}
private void processTask(String task) {
getLog().info("Processing task: " + task + " on thread: " + Thread.currentThread().getName());
try {
// 模拟任务处理
Thread.sleep(1000);
getLog().info("Completed task: " + task);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
getLog().warn("Task interrupted: " + task);
}
}
}
第五部分:测试和调试
5.1 单元测试
package com.example;
import org.apache.maven.plugin.testing.AbstractMojoTestCase;
import org.junit.Test;
import java.io.File;
public class HelloMojoTest extends AbstractMojoTestCase {
@Override
protected void setUp() throws Exception {
super.setUp();
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
}
@Test
public void testMojoExecution() throws Exception {
File pom = getTestFile("src/test/resources/test-pom.xml");
assertNotNull(pom);
assertTrue(pom.exists());
HelloMojo mojo = (HelloMojo) lookupMojo("sayhello", pom);
assertNotNull(mojo);
// 设置参数
setVariableValueToObject(mojo, "message", "Test Message");
setVariableValueToObject(mojo, "repeat", 2);
mojo.execute();
}
}
5.2 集成测试
<!-- 在pom.xml中添加集成测试支持 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-invoker-plugin</artifactId>
<version>3.2.2</version>
<configuration>
<projectsDirectory>src/it</projectsDirectory>
<cloneProjectsTo>${project.build.directory}/it</cloneProjectsTo>
</configuration>
<executions>
<execution>
<id>integration-test</id>
<goals>
<goal>install</goal>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
第六部分:最佳实践
6.1 错误处理
public class RobustMojo extends AbstractMojo {
@Parameter(property = "failOnError", defaultValue = "true")
private boolean failOnError;
@Parameter(property = "maxRetries", defaultValue = "3")
private int maxRetries;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
int retryCount = 0;
while (retryCount <= maxRetries) {
try {
performOperation();
return; // 成功则退出
} catch (Exception e) {
retryCount++;
if (retryCount > maxRetries) {
if (failOnError) {
throw new MojoExecutionException("Operation failed after " + maxRetries + " retries", e);
} else {
getLog().error("Operation failed but continuing due to configuration", e);
return;
}
} else {
getLog().warn("Operation failed, retrying (" + retryCount + "/" + maxRetries + ")");
try {
Thread.sleep(1000 * retryCount); // 指数退避
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new MojoExecutionException("Operation interrupted", ie);
}
}
}
}
}
private void performOperation() throws Exception {
// 具体的操作逻辑
}
}
6.2 日志记录
public class LoggingMojo extends AbstractMojo {
@Parameter(property = "logLevel", defaultValue = "INFO")
private String logLevel;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
// 根据配置调整日志级别
switch (logLevel.toUpperCase()) {
case "DEBUG":
getLog().debug("Debug level logging enabled");
break;
case "INFO":
getLog().info("Info level logging enabled");
break;
case "WARN":
getLog().warn("Warn level logging enabled");
break;
case "ERROR":
getLog().error("Error level logging enabled");
break;
}
// 结构化日志记录
logStructuredInfo();
}
private void logStructuredInfo() {
getLog().info("=== Plugin Execution Summary ===");
getLog().info("Timestamp: " + java.time.Instant.now());
getLog().info("Java Version: " + System.getProperty("java.version"));
getLog().info("OS: " + System.getProperty("os.name"));
getLog().info("=================================");
}
}
总结
这个教程从基础的Maven插件概念开始,逐步深入到高级功能开发:
- 基础: 创建简单插件、参数配置
- 中级: 访问项目信息、文件操作、复杂配置
- 高级: 生命周期绑定、依赖注入、多线程处理
- 工程化: 测试、调试、最佳实践
通过这个由浅入深的学习路径,你可以掌握Maven插件开发的完整技能栈,创建出功能强劲、稳定可靠的Maven插件。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
相关文章
暂无评论...


