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

如何将命令行参数传给Weld SE容器以实现CDI托管依赖该参数的类?

可通过Weld SE托管命令行参数的实现方案

我有一个可通过命令行启动的应用,启动命令为 java -jar MyAssembledJarWithAllDep.jar -foo bar。
我正在使用Weld SE实现Jakarta EE CDI规范,同时用Apache Commons CLI工具解析命令行参数。

现有Maven依赖配置

<dependency>
    <groupId>jakarta.enterprise</groupId>
    <artifactId>jakarta.enterprise.cdi-api</artifactId>
    <version>3.0.0</version>
    <scope>provided</scope>
</dependency>

<dependency>
    <groupId>org.jboss.weld.se</groupId>
    <artifactId>weld-se</artifactId>
    <version>2.4.6.Final</version>
</dependency>

<dependency>
    <groupId>commons-cli</groupId>
    <artifactId>commons-cli</artifactId>
    <version>1.4</version>
</dependency>

当前的代码实现

Weld容器初始化代码

public static void main(String[] args) {
    Weld weld = new Weld();
    WeldContainer container = weld.initialize();
    container.select(MyRunnerClass.class).get().run(args);
    container.shutdown();
}

现有参数解析逻辑

public void run(String[] args) {
   CommandLineParser clp = new CommandLineParser(args);
   clp.parse();
}

待解决的核心问题

当前自行实例化CommandLineParser的方式无法纳入CDI托管,无法充分利用CDI的拦截、注入、生命周期管理能力,需要实现将命令行参数传递给Weld容器,让Weld可以自行创建依赖参数的CommandLineParser类实例。

落地实现步骤

核心思路是把命令行参数作为CDI托管bean暴露出去,让Weld能在容器初始化阶段拿到参数,后续所有需要参数、需要CLI解析器的注入点都直接从容器获取,不需要手动实例化对象。

  • 第一步:定义自定义CDI限定符,用来标记命令行来源的参数,避免和其他同类型的bean产生注入冲突
    代码示例:
    import jakarta.enterprise.inject.Qualifier;
    import java.lang.annotation.*;
    
    @Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
    @Retention(RetentionPolicy.RUNTIME)
    @Qualifier
    public @interface CliArgs {}
    
  • 第二步:编写临时参数持有器,在main方法拿到命令行参数后先暂存,供Weld启动阶段读取。注意这个持有器仅做参数传递,不能在业务代码中直接调用。
    代码示例:
    public class CliArgsHolder {
        private static String[] appArgs;
        public static void setArgs(String[] args) {
            appArgs = args;
        }
        public static String[] getArgs() {
            return appArgs;
        }
    }
    
  • 第三步:编写CDI扩展,在容器启动的bean发现阶段,把暂存的参数注册为可注入的bean,同时把解析好的CommandLine对象也纳入容器托管。
    首先写生产者类,负责生成可注入的参数和解析结果:
    import jakarta.enterprise.inject.Produces;
    import org.apache.commons.cli.*;
    
    public class CliBeanProducer {
        // 生产原始命令行参数,用自定义的@CliArgs限定符标记
        @Produces
        @CliArgs
        public String[] produceRawArgs() {
            return CliArgsHolder.getArgs();
        }
    
        // 生产解析完成的CommandLine对象,业务代码可以直接注入使用
        @Produces
        public CommandLine produceParsedCommand(@CliArgs String[] rawArgs) throws ParseException {
            // 在这里定义你自己的CLI参数规则,比如示例中的-foo参数
            Options cliOptions = new Options();
            cliOptions.addOption("foo", true, "示例自定义参数");
            return new DefaultParser().parse(cliOptions, rawArgs);
        }
    }
    
    然后写CDI扩展类,让Weld启动时自动加载上面的生产者:
    import jakarta.enterprise.event.Observes;
    import jakarta.enterprise.inject.spi.BeforeBeanDiscovery;
    import jakarta.enterprise.inject.spi.BeanManager;
    
    public class CliCdiExtension {
        public void onBeforeBeanDiscovery(@Observes BeforeBeanDiscovery event, BeanManager bm) {
            event.addAnnotatedType(bm.createAnnotatedType(CliBeanProducer.class));
        }
    }
    
    最后在项目的src/main/resources/META-INF/services/目录下,创建名为jakarta.enterprise.inject.spi.Extension的文件,文件内容填上面扩展类的全限定名,Weld启动时会自动扫描加载。
  • 第四步:修改main方法的初始化逻辑,不再手动给业务类传递参数,完全交给CDI容器管理依赖
    修改后的main方法示例:
    public static void main(String[] args) {
        // 先把命令行参数暂存,等Weld启动时自动读取
        CliArgsHolder.setArgs(args);
    
        Weld weld = new Weld();
        WeldContainer container = weld.initialize();
        // 直接从容器获取业务类实例,不需要手动传参
        container.select(MyRunnerClass.class).get().run();
        container.shutdown();
    }
    
  • 第五步:业务代码中通过@Inject直接获取解析好的CLI参数,无需手动创建解析器实例
    业务类示例:
    import jakarta.inject.Inject;
    import org.apache.commons.cli.CommandLine;
    
    public class MyRunnerClass {
        // 直接注入容器托管的解析结果
        @Inject
        CommandLine parsedCli;
    
        public void run() {
            // 直接读取参数值即可
            String fooParamValue = parsedCli.getOptionValue("foo");
            // 后续业务逻辑...
        }
    }
    

注意事项

  • 这里存在一个依赖兼容问题:当前你引入的weld-se 2.4.6.Final是基于javax命名空间的旧版本,和你引入的jakarta.enterprise.cdi-api 3.0.0(jakarta命名空间)存在类加载冲突,会导致扩展加载失败、注入报错,建议升级到Weld SE 4.x系列的匹配版本,对应Jakarta CDI 3.0的规范要求。
  • 自定义的@CliArgs限定符是必须的,因为String[]属于通用类型,如果没有限定符,Weld在注入时可能会找不到匹配的bean,或者误注入其他同类型的数组对象。
  • 不要把静态的CliArgsHolder当成全局变量在业务代码中直接使用,它的作用仅仅是在main方法和Weld扩展之间做临时的参数传递,所有业务逻辑中用到的CLI参数都要通过CDI注入的方式获取,保证依赖的可测试性和托管能力。

内容的提问来源于stack exchange,提问作者Wumba

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.26 03:45:04