使用Okta的身份管理平台轻松部署您的应用程序 使用Okta的API在几分钟之内即可对任何应用程序中的用户进行身份验证,管理和保护。 今天尝试Okta。
由于其插件生态系统的普及,Apache Maven仍然是Java领域中最流行的构建工具。 很容易找到一个现有的插件来完成您的应用程序所需的几乎所有事情,从确保源文件具有许可证标头到验证版本之间的二进制兼容性。 有时,您需要编写一个自定义插件来满足产品中的要求。
在本教程中,我将向您展示如何构建一个简单的Maven插件来解析项目的当前Git哈希,即git rev-parse --short HEAD
。
在开始之前,请确保安装Java 8和Apache Maven 。 我使用SDKMAN来安装它们。
如果您想观看视频, 我创建了此博客文章的截屏视频 。
创建一个新的Maven项目
我将使用Maven构建新的Maven插件也就不足为奇了。 您可以使用自己喜欢的IDE来创建一个新项目,但是为了简单起见,我将手动创建一个新的pom.xml
文件(在名为example-maven-plugin
的新目录中:
<?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&>
<modelVersion>4.0.0</modelVersion>
<groupId>com.okta.example</groupId>
<artifactId>example-maven-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>maven-plugin</packaging>
<name>Example Maven Plugin</name>
<description>An Example Maven Plugin</description>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
这是因为简单,因为它得到,我已经定义了的Maven GAV(G roup ID,A rtifact ID,V版为),名称,以及最重要的是我所设定的packaging
到maven-plugin
。 尽管组ID几乎可以是任何东西,但强烈建议使用反向域名表示法,类似于Java包 。
添加Maven依赖项
接下来,我需要定义一些对maven-core
, maven-plugin-api
和maven-plugin-annotations
依赖。 这些都按照provided
范围限定,这意味着当插件运行时,使用的实际版本将取决于您安装的Apache Maven的版本。
<dependencies>
<dependency>
<!-- plugin interfaces and base classes -->
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>3.6.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<!-- needed when injecting the Maven Project into a plugin -->
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
<version>3.6.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<!-- annotations used to describe the plugin meta-data -->
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>3.5</version>
<scope>provided</scope>
</dependency>
</dependencies>
插件构建插件
插件是什么实际上给Maven的它的力量,其核心Maven是只是一个插件框架,所以很自然,我将使用Maven插件来构建一个Maven插件与Maven插件插件 。 乌龟一直向下!
![](https://i-blog.csdnimg.cn/blog_migrate/fceffbbc913b05919ab4b28588c14a0d.png)
maven-plugin-plugin
实际上是自动定义的,因为我使用了上面的maven-plugin
的包装类型,要使用较新的版本,我可以在pluginManagment
部分中更新插件:
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>3.6.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<version>3.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</project>
我还包括了Maven Site Plugin,它是可选的,在后面的文章中会有更多介绍。
这就对了! 如果您想一次复制并粘贴整个文件,可以从GitHub上获取它。
编写Maven插件代码
在有趣的部分,编写代码! 一个Maven插件实际上是一个或多个“目标”的集合。 每个目标是由一个Java类中定义被称为“魔”(M Aven的平原öLD 的J avaöbject)。
创建一个新类: src/main/java/com/okta/example/maven/GitVersionMojo.java
package com.okta.example.maven;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
/**
* An example Maven Mojo that resolves the current project's git revision and adds
* that a new {@code exampleVersion} property to the current Maven project.
*/
@Mojo(name = "version", defaultPhase = LifecyclePhase.INITIALIZE)
public class GitVersionMojo extends AbstractMojo {
public void execute() throws MojoExecutionException, MojoFailureException {
// The logic of our plugin will go here
}
}
没什么大不了的,现在我有了一个新的Maven插件,它有一个名为version
目标。 该项目将在初始化项目时执行。 有几个生命周期可供选择,在本例中,我正在使用“初始化”,因为我希望我的插件在其他插件之前运行。 如果要创建一个插件来创建新文件,则可能需要使用“ generate-resources”阶段。 请查看生命周期参考文档 ,以获取其他阶段的描述。
此时,我可以使用mvn install
构建项目,然后使用以下命令执行插件:
# mvn ${groupId}:${artifactId}:${goal}
mvn com.okta.example:example-maven-plugin:version
但是,由于execute
方法为空,因此实际上还不会做任何事情。
添加Maven参数
为了使该插件实际起作用,我将添加几个参数。 Maven参数在MOJO类中定义为字段:
/**
* The git command used to retrieve the current commit hash.
*/
@Parameter(property = "git.command", defaultValue = "git rev-parse --short HEAD")
private String command;
@Parameter(property = "project", readonly = true)
private MavenProject project;
值得注意的是,Javadoc对于Maven插件很重要,因为在生成特定于插件的文档时将使用它。 由于我们都是优秀的开发人员,因此我们永远不会忘记添加文档,对吗?
Parameter
注释告诉Maven向该字段中注入一个值。 这类似于Spring的Value
注释。 对于command
字段,我将property
值设置为git.command
。 这允许用户使用标准-D
表示法在命令行上更改值:
mvn com.okta.example:example-maven-plugin:version \
-Dgit.command="git rev-parse --short=4 HEAD"
注入MavenProject
以便直接读取或修改项目中的某些内容也是很常见的。 例如, MavenProject
使您可以访问依赖项以及pom.xml
定义的任何内容。 就我而言,我将添加一个额外的属性,该属性可以在以后的构建中使用。
用Java执行命令
现在我们有了command
参数,我们需要执行它! 定义一个新的getVersion
方法来处理此逻辑:
public String getVersion(String command) throws MojoExecutionException {
try {
StringBuilder builder = new StringBuilder();
Process process = Runtime.getRuntime().exec(command);
Executors.newSingleThreadExecutor().submit(() ->
new BufferedReader(new InputStreamReader(process.getInputStream()))
.lines().forEach(builder::append)
);
int exitCode = process.waitFor();
if (exitCode != 0) {
throw new MojoExecutionException("Execution of command '" + command
+ "' failed with exit code: " + exitCode);
}
// return the output
return builder.toString();
} catch (IOException | InterruptedException e) {
throw new MojoExecutionException("Execution of command '" + command
+ "' failed", e);
}
}
这使用Java的内置Runtime.exec()
并捕获输出文本。 任何异常都将作为MojoExecutionException
(这将导致生成失败。)
接下来更新execute()
方法:
public void execute() throws MojoExecutionException, MojoFailureException {
// call the getVersion method
String version = getVersion(command);
// define a new property in the Maven Project
project.getProperties().put("exampleVersion", version);
// Maven Plugins have built in logging too
getLog().info("Git hash: " + version);
}
就是这样,现在我们只需要使用插件!
Maven插件的用法
到目前为止,我一直在使用以下命令直接执行插件:
mvn com.okta.example:example-maven-plugin:version
通常,插件会添加到pom.xml
因此它们会作为构建的一部分自动运行。 为了说明这一点,我将使用以下pom.xml
(在另一个目录中)创建一个新的Maven项目:
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.okta.example</groupId>
<artifactId>example-usage</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<build>
<plugins>
<plugin>
<groupId>com.okta.example</groupId>
<artifactId>example-maven-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<configuration>
<!-- optional, the command parameter can be changed here too -->
<command>git rev-parse --short=4 HEAD</command>
</configuration>
<executions>
<execution>
<goals>
<goal>version</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.github.ekryd.echo-maven-plugin</groupId>
<artifactId>echo-maven-plugin</artifactId>
<version>1.2.0</version>
<inherited>false</inherited>
<executions>
<execution>
<id>end</id>
<goals>
<goal>echo</goal>
</goals>
<phase>process-resources</phase>
<configuration>
<message>${line.separator}${line.separator}
The project version is ${project.version}-${exampleVersion}
${line.separator}
</message>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
在该项目结果上运行mvn package
将给出输出:
![](https://i-blog.csdnimg.cn/blog_migrate/117f8e082d79a5817a8605767a7c3b75.png)
[INFO] Git hash: 1ab3
行显示了我的插件执行时,该插件定义的新exampleVersion
属性由echo-maven-plugin
注意:将插件添加到
pom.xml
,可以使用简写形式执行该插件:mvn <prefix>:<goal>
,通常,“ prefix”是工件ID减去“ -maven-plugin” 。 例如mvn example:version
。
Maven插件中的依赖注入
我们的插件很棒,而且很不错,但是所有代码都挤在一个文件中。 我喜欢将代码分解为易于测试的块。 输入Sisu ,将建立Maven容器。 Sisu是一个基于Guice的IoC容器,它是Spring的替代品。
这一切的真正含义是,我可以使用标准的JSR-330( @Inject
)注释来破坏我的代码,而不必担心IoC容器的细节!
在src/main/java/com/okta/example/maven/VersionProvider.java
创建一个新接口:
package com.okta.example.maven;
import org.apache.maven.plugin.MojoExecutionException;
public interface VersionProvider {
String getVersion(String command) throws MojoExecutionException;
}
并将Runtime.exec
逻辑从GitVersionMojo
到新类src/main/java/com/okta/example/maven/RuntimeExecVersionProvider.java
:
package com.okta.example.maven;
import org.apache.maven.plugin.MojoExecutionException;
import javax.inject.Named;
import javax.inject.Singleton;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.concurrent.Executors;
@Named
@Singleton
public class RuntimeExecVersionProvider implements VersionProvider {
@Override
public String getVersion(String command) throws MojoExecutionException {
try {
StringBuilder builder = new StringBuilder();
Process process = Runtime.getRuntime().exec(command);
Executors.newSingleThreadExecutor().submit(() ->
new BufferedReader(new InputStreamReader(process.getInputStream())).lines().forEach(builder::append)
);
int exitCode = process.waitFor();
if (exitCode != 0) {
throw new MojoExecutionException("Execution of command '" + command + "' failed with exit code: " + exitCode);
}
// return the output
return builder.toString();
} catch (IOException | InterruptedException e) {
throw new MojoExecutionException("Execution of command '" + command + "' failed", e);
}
}
}
我添加了标准Java @Named
和@Singleton
批注,以将该类标记为由IoC容器管理的单例。 这等效于使用Spring的@Component
。
现在只需更新GitVersionMojo
即可注入VersionProvider
:
@Inject
private VersionProvider versionProvider;
public void execute() throws MojoExecutionException, MojoFailureException {
String version = versionProvider.getVersion(command);
project.getProperties().put("exampleVersion", version);
getLog().info("Git hash: " + version);
}
这就对了! 您可以像以前一样构建和运行插件,并获得相同的结果。
一件事,文档!
关于Maven,我最喜欢的事情之一是插件具有一致的文档结构。 要生成文档,请在pom.xml
添加一个新的reporting
部分:
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<reportSets>
<reportSet>
<reports>
<report>report</report>
</reports>
</reportSet>
</reportSets>
</plugin>
</plugins>
</reporting>
您还应该向项目添加更多元数据,但这是可选的。 例如,我将添加组织和前提条件,因为这些条件和前提条件已包含在生成的站点中:
<organization>
<name>Example, Inc</name>
<url>https://google.com/search?q=example.com</url>
</organization>
<prerequisites>
<maven>3.5.0</maven>
</prerequisites>
现在只需运行mvn site
即可生成文档! 在浏览器中打开target/site/plugin-info.html
。 明白为什么所有Javadoc都那么重要吗?
![1- Maven插件](https://i-blog.csdnimg.cn/blog_migrate/4090df230c1d2ced2a37756b61f4c236.png)
找到更多
与往常一样,您可以在GitHub上找到本教程的完整源代码。 要了解有关构建插件的更多信息, Apache Maven项目提供了出色的文档。 还可以查看以下其他教程:
- 使用Okta Maven插件在几秒钟内开始使用Okta
- Spring Boot登录选项快速指南
- 使用Hamcrest使Java测试Groovy
- 许可Maven插件
- japicmp –一个用于二进制,源代码和semver验证的Maven插件
如果您喜欢本教程,请在Twitter @oktadev上关注我们。 我们还将每周在YouTube频道上发布视频教程。
使用Okta的身份管理平台轻松部署您的应用程序 使用Okta的API在几分钟之内即可对任何应用程序中的用户进行身份验证,管理和保护。 今天尝试Okta。
翻译自: https://www.javacodegeeks.com/2019/11/how-to-build-maven-plugin.html