能否在Quarkus.run调用前使用CDI?基于Picocli的Quarkus CLI应用依赖注入问题
能否在Quarkus.run调用前使用CDI?基于Picocli的Quarkus CLI应用依赖注入问题
你遇到的这个问题其实挺典型的——用Picocli做Quarkus CLI的命令解析,想在触发Quarkus.run()之前就注入CDI bean,但默认情况下Quarkus的CDI容器在Quarkus.run()调用前确实没初始化好对吧?我来给你捋捋可行的解决思路。
核心问题在于:Quarkus的CDI容器默认是在Quarkus.run()执行时才完成初始化的,直接在main方法里手动new命令实例的话,CDI根本没机会为你注入依赖。不过Quarkus官方其实已经考虑到了这种CLI场景,有专门的集成方式来解决这个问题。
推荐解决方案:让Picocli命令成为CDI托管bean
你可以把Picocli的命令类定义为CDI bean,然后借助Quarkus提供的Quarkus.run()重载方法,让CDI容器先初始化完成,再处理命令解析逻辑。这样不管是start还是stop命令,都能正常使用依赖注入,哪怕是在启动Quarkus服务器之前。
给你改一下示例代码,你可以参考:
import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import io.quarkus.runtime.Quarkus; import io.quarkus.runtime.annotations.QuarkusMain; import jakarta.inject.Inject; import picocli.CommandLine; import picocli.CommandLine.Command; @QuarkusMain public class MyApp { public static final Path PID_FILE = Path.of("myapp.pid"); // 注入我们的主命令CDI bean @Inject MainCommand mainCommand; public static void main(String... args) { // 使用Quarkus的run重载方法,它会先初始化CDI容器再执行lambda内的逻辑 Quarkus.run(args, (context, commandLineArgs) -> { int exitCode = new CommandLine(mainCommand).execute(commandLineArgs); System.exit(exitCode); }); } // 主命令类:标记为CDI的Dependent作用域bean @jakarta.enterprise.context.Dependent @Command(name = "myapp", mixinStandardHelpOptions = true, subcommands = {StartCommand.class, StopCommand.class}) public static class MainCommand { // 这里可以注入你需要的任何CDI bean @Inject YourCustomBean customBean; // 主命令默认逻辑(比如输出帮助) @CommandLine.Command public int run() { System.out.println("请使用start或stop子命令"); return 0; } } @Command(name = "start", description = "启动Quarkus HTTP服务器") @jakarta.enterprise.context.Dependent public static class StartCommand implements Runnable { // 注入自定义CDI bean @Inject YourCustomBean customBean; @Override public void run() { // 可以在这里先使用customBean做一些初始化操作 customBean.doPreStartLogic(); try { // 写入PID文件 Files.writeString(PID_FILE, String.valueOf(ProcessHandle.current().pid())); } catch (IOException e) { e.printStackTrace(); } // 启动Quarkus服务器 Quarkus.run(); } } @Command(name = "stop", description = "停止Quarkus HTTP服务器") @jakarta.enterprise.context.Dependent public static class StopCommand implements Runnable { // 同样可以注入CDI bean @Inject YourCustomBean customBean; @Override public void run() { // 用customBean处理一些停止前的逻辑 customBean.doPreStopLogic(); try { String pidStr = Files.readString(PID_FILE); long pid = Long.parseLong(pidStr); ProcessHandle.of(pid).ifPresent(ProcessHandle::destroy); Files.deleteIfExists(PID_FILE); } catch (IOException e) { e.printStackTrace(); } } } // 自定义CDI bean示例 @jakarta.enterprise.context.Dependent public static class YourCustomBean { public void doPreStartLogic() { System.out.println("启动前执行自定义逻辑"); } public void doPreStopLogic() { System.out.println("停止前执行自定义逻辑"); } } }
方案原理说明
- 当你调用
Quarkus.run()的这个重载方法时,Quarkus会先完成CDI容器的初始化,再执行你传入的lambda表达式 - 所有标记了CDI作用域(比如
@Dependent)的Picocli命令类,都会被CDI容器管理,它们的@Inject依赖会被自动注入 - 不管是需要启动服务器的
start命令,还是不需要启动的stop命令,都能正常使用CDI bean的功能
额外注意事项
- 一定要给你的Picocli命令类加上CDI作用域注解,比如
@Dependent,否则CDI不会托管这些类 - 不要在main方法里直接new命令实例,必须通过CDI注入获取,这样依赖才能被正确注入
- 这种方式是Quarkus官方推荐的Picocli集成方式,比手动初始化CDI容器要简洁可靠得多
备注:内容来源于stack exchange,提问作者Dorian V.
相关产品推荐
相关产品推荐

