一个简单的Maven插件的编写方法

我在用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就可以了,十分简单。

Fantastic Soft

风铃之书是个人的工作和生活的总结和分享的站点,欢迎来访和留言,有时也会提供自家软件的发布版本和开源项目。