我有两个Maven插件,A
和B
。它们是独立开发的,但它们恰好需要相同的配置选项。大多数潜在用户将安装插件A
,插件B
是它的补充。
我正在尝试寻找一种在插件之间共享配置的方法。我知道您可以将配置选项提升为属性,然后为每个插件重用它们,但这仍然要求用户为每个插件的每个选项配置一个配置条目,这是一种样板。
此问题的目标是找到一种跨插件共享配置的方法,从而减少可能的冗余。这可能吗?
起初,我认为这是不可能的,但通过深入研究Maven的内部,我意识到有一个解决方案。这个解决方案很简单 - 我们不必自己重新实现Maven内部的部分,我们可以重用底层API。
诀窍是使用Maven内部来修改需要配置另一个Mojo的Mojo的配置(在这种情况下,B
需要来自A
的配置)。
为此,首先我们需要为B
设置一个mojo定义,其中包含parameter
接受的所有配置值(A
s)。然后,我们将在B
中定义以下四个额外字段(如果它们尚未在A
中定义),因为它们将是实际实现所必需的:
@Mojo(name = "B", requiresProject = true) // and the rest of the config
public class B extends AbstractMojo {
@Parameter(defaultValue = "${project}", readonly = true, required = true)
private MavenProject project;
@Parameter(defaultValue = "${session}", readonly = true, required = true)
private MavenSession session;
@Parameter(defaultValue = "${mojoExecution}", readonly = true, required = true)
private MojoExecution mojoExecution;
@Component
private MavenPluginManager mavenPluginManager;
// and the rest of the properties here...
public void execute() throws MojoExecutionException {
B initializedMojo = ScalaImplementation.apply(project, session, mojoExecution, mavenPluginManager, encoding);
// Implement plugin logic with `initializedMojo` instead of `this`.
}
}
有了这四个新字段,我们可以通过以下方式实现ScalaImplementation
(我选择在Scala代码中实现它):
object ScalaImplementation {
def apply(project: MavenProject,
session: MavenSession,
mojoExecution: MojoExecution,
mavenPluginManager: MavenPluginManager): B = {
val currentConfig = mojoExecution.getConfiguration()
val pluginA = Option(project.getBuild().getPluginsAsMap().get("groupIdA:artifactIdA"))
.getOrElse(sys.error(s"Plugin A could not be found."))
val configA = pluginA.getConfiguration().asInstanceOf[Xpp3Dom]
mojoExecution.setConfiguration(Xpp3Dom.mergeXpp3Dom(currentConfig, configA))
val initializedMojoB = mavenPluginManager
.getConfiguredMojo(classOf[Mojo], session, mojoExecution)
.asInstanceOf[B] // Note that this is safe.
initializedMojoB
}
}
请注意,要使此解决方案起作用,mojo B必须没有任何替代必需字段,该字段不在A中且没有默认值(否则Maven会抱怨它无法将其初始化为null
)。
我们所做的是通过结合插件A
和插件B
的配置来欺骗Maven mojo引擎,这些配置在运行时可以访问。首先,我们得到了一个半初始化的mojo,只有我们关心的字段。然后我们将它传递给一个相对简单的方法,该方法负责设置当前mojoExecution
的配置,并且我们实例化了一个新的mojo B
。由于这个新的mojo具有A
和B
的配置值,因此这个时间被完全初始化并准备好使用。