You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否在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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 11:49:32