起初,我认为这是不可能的,但通过深入研究 Maven 的内部结构,我意识到有一个解决方案。这个解决方案很简单——我们不必自己重新实现部分 Maven 内部,我们可以重用底层 API。
解决方案
诀窍是使用 Maven 内部修改需要另一个 Mojo 配置的 Mojo 的配置(在这种情况下,B 需要来自 A 的配置)。
为此,首先我们需要为B 定义一个mojo 定义,其中包含A 接受的所有配置值(parameters)。然后,我们将在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 的配置值,所以这个时间已经完全初始化并可以使用了。