我在用Maven的时候,经常会遇到一些插件,通过插件能够拓展Maven的功能,例如Springboot的打包就是通过插件完成的。插件是很实用的东西,所以我目前也准备做一个。为什么我要做这个呢?那是因为我发现了一个好东西,GraalVM,他也是一个虚拟机,可以运行java字节码,同时它还可以做到一件事,将java编译为原生的本地代码,就像C语言那样,虽然我很早就发现这个了,不过大多数都只在Linux上面应用。而且他早期不支持JavaFX,我就没有多少动力去尝试。不过前几天我发现它支持javafx了!但是这个东西因为是要静态编译,所以反射是用不了的,得额外配置,所以我就想,能不能做个东西,简单粗暴的生成我需要的反射配置文件?从前我没有写过Maven插件,所以在这里留个笔记。Maven版本:3.8开发环境:IDEA
Step 1 :
我们需要建立一个Maven工程,使用Maven-Plugin-Plugin和Maven的MojoAPI,MavenCoreApi。然后依据需要添加自己的依赖。
<!-- Pom 示例 -->
<?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">
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<encoding>UTF-8</encoding>
<java.version>11</java.version>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<modelVersion>4.0.0</modelVersion>
<groupId>这里是GropeId</groupId>
<artifactId>这里是ArtifactId</artifactId>
<packaging>maven-plugin</packaging>
<version>这里是插件版本</version>
<dependencies>
<!--Maven 核心API-->
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
<version>3.6.0</version>
</dependency>
<!--Maven 注解处理器 -->
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>3.6.0</version>
<scope>provided</scope>
</dependency>
<!-- Maven Mojo API -->
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>3.6.0</version>
</dependency>
<!-- 类扫描器依赖,可以看你习惯自己挑 -->
<dependency>
<groupId>io.github.classgraph</groupId>
<artifactId>classgraph</artifactId>
<version>4.8.102</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>3.6.0</version>
</plugin>
</plugins>
</build>
</project>
<!-- -->
大概像这样,maven的plugin工程就准备好了。
Step 2
编写继承自AbstractMojo的类,在这个类的抽象方法execute里面处理我们想做的事情。继承自AbstractMojo的类型,需要一个Mojo注解,他是插件的描述信息,包括插件的名字等很多属性,Maven会通过它为插件生成一个描述符,所以需要在Class上面标注这个注解。Maven本身内置了一个容器,所以可以通过依赖注入的方式获取需要的组件,依赖注入需要注解“Inject”JSR-330的那个注入注解。
// 省略类的其他部分,只看字段
// 通过Inject注解注入Maven的对象。
@Inject
private MavenProject project;
@Inject
private RepositorySystem system;
//
就像这样,上面两个字段一个是MavenProject,一个是RepositorySystem,对于实现我的目的——生成Graal需要的反射信息,比较重要的也就是他们。通过Project可以得到工程的基本信息,例如Pom的路径,编译的源文件夹和输出文件夹(project.getBuild里面),依赖信息(Dependency,就是pom里面的那些依赖,也可以得到这些依赖的包的路径)等。如果想要添加额外的参数,我们需要使用Parameter注解,这个注解需要标注到字段上,它支持普通的值,也支持数组和map,配置需要在插件使用的时候写在Pom的Plugin的configuration里面。例如这种:
// 省略类的其他部分,只看字段
// 通过Parameter注解注入插件配置的值。
@Parameter(property = "target")
private String filePath;
@Parameter(property = "iconPath")
private String iconPath;
@Parameter(required = true,property = "packages")
private String[] packages;
//
在使用插件的时候就需要这样配置:

Step 3
解析依赖也是一个重要的部分,这里需要如下处理:
// 解析Maven的依赖
List<Artifact> artifacts = new ArrayList<>();
for (Dependency dependency : project.getDependencies()) {
Artifact artifact = system.createDependencyArtifact(dependency);
ArtifactResolutionRequest resolutionRequest = new ArtifactResolutionRequest();
resolutionRequest.setArtifact(artifact);
ArtifactResolutionResult resolved = system.resolve(resolutionRequest);
if (resolved.getArtifacts().size() > 0) {
artifacts.addAll(resolved.getArtifacts());
}
}
//
通过注入的RepositorySystem字段创建Request,然后使用从Project的依赖获取到的依赖对象,通过解析就能得到Artifact。Artifact可以获取到Jar包具体的文件对象,这样就可以获取一个Pom工程完整的ClassPath或者ModulePath了,如果要在插件里面编译什么东西,他们就是必不可少的。
Step 4
很多maven的插件会跟随某一个maven的指令(phase)自动执行,例如Maven的compile之类的,为了做到这个,需要在使用插件的时候,在他的Plugin的标签里面添加executions,里面放execution,每一个execution需要一个goals标签和一个id标签,就像这样:

这个标签同样可以添加一个phase,这样我们自定义的插件的goal会在phase执行后被调用,如果有多个插件都在使用同一个phase,那么他们执行的顺序就是他们在pom文件中排列的先后顺序,Plugin标签排列在前面的插件的goals将会先于排列在后面的插件的goals进行执行。
End.
到目前为止,我没有用到更多的功能,而且关于maven插件编写方式的资料比较少,尤其是这种比较新的版本,以后如果用到其他功能,我会更新到这个里面。最终,在得到输出路径之后,我就能扫描类的路径,获取需要反射的类,然后编写对应的配置输出到src和target就可以了,十分简单。




