如何使用多个入口点打包Java应用程序

如何使用多个入口点打包Java应用程序,java,docker,gradle,jar,Java,Docker,Gradle,Jar,我一直在努力理解如何打包我们的应用程序,以便我们可以在一个Docker映像中运行多个命令 我们的设置 bootstrap/Main是注册其他命令的入口点,即HttpServer、CliCommandOne、CliCommandTwo等。这些命令可以在其他模块/包中定义。该应用程序打包为带有Gradle应用程序插件的JAR。默认情况下,如果没有参数,将使用HttpServer命令。这是可能的,因为在HttpServer命令中,我们可以显式地启动HTTP服务器(我们现在使用的是框架) Docker映

我一直在努力理解如何打包我们的应用程序,以便我们可以在一个Docker映像中运行多个命令

我们的设置

bootstrap/Main
是注册其他命令的入口点,即
HttpServer
CliCommandOne
CliCommandTwo
等。这些命令可以在其他模块/包中定义。该应用程序打包为带有Gradle应用程序插件的JAR。默认情况下,如果没有参数,将使用
HttpServer
命令。这是可能的,因为在
HttpServer
命令中,我们可以显式地启动HTTP服务器(我们现在使用的是框架)

Docker映像已部署到k8s。因此,我们有一个正在运行的服务器,同时,我们可以
exec
进入容器并运行另一个CLI命令

问题

我们希望切换到另一个框架,例如Quarkus、Micronaut或Spring。看起来这些框架要么允许您启动HTTP服务器(或WebSocket),要么创建CLI命令,但无法复制我们现在拥有的功能,即在一个JAR中打包多个命令,并能够在一个Docker映像中启动它们

我们想到的解决方案

我能想到的是Kafka正在使用的一种方法:据我所知,他们只有一个JAR,然后使用大量的
sh
scripts()来运行不同的类。这对我们来说似乎太个性化了

当然,我们可以使用
main
方法为每个类生成单独的JAR,然后创建不同的Docker映像,并以某种方式找到运行它们的方法。但就Docker图像而言,这似乎是一项开销。如果我们需要20个命令呢


因此,我正在寻找一种方法,如何包装应用程序,以便有多个可执行的“命令”。我甚至不确定这是不是个好主意。很高兴听到可能的选择或最佳实践。

我在Micronaut找到了一种方法

控制器:

@Controller("/hello")
public class MyController {
    @Get(produces = MediaType.TEXT_PLAIN)
    public String hello() {
        return "hello";
    }
}
HTTP服务器命令:

@Command(name = "http-server", description = "Starts HTTP server")
public class HttpApp implements Runnable {
    @Override
    public void run() {
        Micronaut.run(HttpApp.class);
    }
}
Cli命令示例:

@Command(name = "cli-one", description = "test cli command one")
public class CliCommandOne implements Runnable {
    @Override
    public void run() {
        System.out.println("hello from CliCommandOne");
    }
}
主要类别:

@Command(name = "main-command",
        description = "Description of a main command",
        mixinStandardHelpOptions = true,
        subcommands = {HttpApp.class, CliCommandOne.class})
public class Application implements Runnable {

    @Override
    public void run() {
        System.out.println("hello from CLI main command");
    }

    public static void main(String[] args) throws Exception {
        PicocliRunner.run(Application.class, args);
    }
}
获取所有可用命令的帮助(使用Gradle):

运行CliCommandOne:

› gw run --args='cli-one'
11:37:32.423 [main] INFO  i.m.context.env.DefaultEnvironment - Established active environments: [cli]
hello from CliCommandOne
启动HTTP服务器:

gw run --args='http-server'
11:37:40.491 [main] INFO  i.m.context.env.DefaultEnvironment - Established active environments: [cli]
11:37:41.263 [main] INFO  io.micronaut.runtime.Micronaut - Startup completed in 307ms. Server Running: http://localhost:8081
<=========----> 75% EXECUTING [2s]
gw运行--args='http-server'
11:37:40.491[主]信息i.m.context.env.DefaultEnvironment-已建立的活动环境:[cli]
11:37:41.263[主]信息io.micronaut.runtime.micronaut-启动在307ms内完成。正在运行的服务器:http://localhost:8081
75%执行[2s]

我不确定自己是否理解具体问题所在。一个jar可以包含任意多的类,每个类可以包含一个main方法。即使您只有一个主方法,是什么阻止您使用参数、系统属性或此主方法中的任何内容来决定要执行的操作(即运行Spring应用程序、Micronaut应用程序或您想要的任何内容)?或者,您可以使用一个main方法创建一个main类,该方法读取第一个命令行参数,并基于此参数将其余参数转发给不同类中的其他几个main方法之一。如果您决定使用一个JAR和多个启动脚本,您可以使用Gradle的
应用程序
插件实现这一点。请参见>使用参数或系统属性或此主要方法中的任何内容来决定要执行的操作,这与我们现在使用picocli所做的类似。我们可以这样做,因为我们的HTTP框架可以通过
app.run()
显式启动。但对于其他框架,我找不到一种方法来做到这一点。我只是对他们没什么经验。基本上,我正在Quarkus或Micronaut中寻找一种方法来执行类似于
webserver.run()
的操作,这样我就可以根据在命令行中获得的参数来执行它。通过阅读这些框架的文档,在我看来,在运行打包JAR之后,它们会隐式地这样做。
gw run --args='http-server'
11:37:40.491 [main] INFO  i.m.context.env.DefaultEnvironment - Established active environments: [cli]
11:37:41.263 [main] INFO  io.micronaut.runtime.Micronaut - Startup completed in 307ms. Server Running: http://localhost:8081
<=========----> 75% EXECUTING [2s]