多个Spring引导CommandLineRunner基于命令行参数

时间:2017-06-11 07:22:36

标签: java spring-boot spring-cloud-task

我已经使用spring cloud任务创建了spring boot应用程序,它应该执行一些命令(任务)。 每个任务/命令都是短期任务,所有任务都从命令行开始,执行一些简短的ETL作业并完成执行。

有一个弹簧启动jar包含所有命令/任务。 每个任务都是CommandLineRunner,我喜欢根据命令行中的参数来决定执行哪些任务(一个或多个)。 这样做的最佳做法是什么? 我不喜欢有脏代码询问“if else”或类似的东西。

3 个答案:

答案 0 :(得分:3)

Spring Boot从应用程序上下文运行所有CommandLineRunnerApplicationRunner bean。你不能选择任何一个args。

所以基本上你有两种可能性:

  1. 您有不同的CommandLineRunner实现,并在每个实现中检查参数以确定是否应运行此特殊CommandLineRunner
  2. 您只实现一个充当调度程序的CommandLineRunner。代码可能如下所示:
  3. 这是您的跑步者将实施的新界面:

    public interface MyCommandLineRunner {
        void run(String... strings) throws Exception;
    }
    

    然后定义实现并使用名称标识它们:

    @Component("one")
    public class MyCommandLineRunnerOne implements MyCommandLineRunner {
        private static final Logger log = LoggerFactory.getLogger(MyCommandLineRunnerOne.class);
    
        @Override
        public void run(String... strings) throws Exception {
            log.info("running");
        }
    }
    

    @Component("two")
    public class MyCommandLineRunnerTwo implements MyCommandLineRunner {
        private static final Logger log = LoggerFactory.getLogger(MyCommandLineRunnerTwo.class);
        @Override
        public void run(String... strings) throws Exception {
            log.info("running");
        }
    }
    

    然后在您的单CommandLineRunner实现中,您将获得应用程序上下文并按名称解析所需的bean,我的示例仅使用第一个参数,并将其称为MyCommandLineRunner.run()方法:

    @Component
    public class CommandLineRunnerImpl implements CommandLineRunner, ApplicationContextAware {
        private ApplicationContext applicationContext;
    
    
        @Override
        public void run(String... strings) throws Exception {
            if (strings.length < 1) {
                throw new IllegalArgumentException("no args given");
            }
    
            String name = strings[0];
            final MyCommandLineRunner myCommandLineRunner = applicationContext.getBean(name, MyCommandLineRunner.class);
            myCommandLineRunner.run(strings);
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.applicationContext = applicationContext;
        }
    }
    

答案 1 :(得分:0)

奇怪的是,没有内置的机制来选择一组CommandLineRunner。默认情况下,所有它们都被执行。

我已经重用了-可能是不正确的-Profile机制,也就是说,我已经用@ org.springframework.context.annotation.Profile(“ mycommand”)注释了每个CommandLineRunner,然后选择了要在系统中执行的那个属性-Dspring.profiles.active = mycommand

有关Profile机制的更多信息,请参阅https://www.baeldung.com/spring-profiles

答案 2 :(得分:0)

类似于此答案https://stackoverflow.com/a/44482525/986160,但使用注入和更少的配置。

有类似的要求,对我有用的是让一个CommandLineApps类实现CommandLineRunner,然后将所有其他命令行运行程序注入为@Component,并使用第一个参数委派给其中一名注射选手。请注意,注入的内容不应扩展CommandLineRunner,也不应标注为@SpringBootAppplication

重要:请小心,尽管如果将呼叫放入crontab中,则如果未完成,则一个呼叫将销毁前一个呼叫。

这里是一个例子:

@SpringBootApplication
public class CommandLineApps implements CommandLineRunner {

    @Autowired
    private FetchSmsStatusCmd fetchSmsStatusCmd;

    @Autowired
    private OffersFolderSyncCmd offersFolderSyncCmd;

    public static void main(String[] args) {
        ConfigurableApplicationContext ctx = SpringApplication.run(CommandLineApps.class,  args);
        ctx.close();
    }

    @Override
    public void run(String... args) {

        if (args.length == 0) {
            return;
        }

        List<String> restOfArgs = Arrays.asList(args).subList(1, args.length);

        switch (args[0]) {
            case "fetch-sms-status":
                fetchSmsStatusCmd.run(restOfArgs.toArray(new String[restOfArgs.size()]));
                break;
            case "offers-folder-sync":
                offersFolderSyncCmd.run(restOfArgs.toArray(new String[restOfArgs.size()]));
                break;
        }
    }
}
@Component
public class FetchSmsStatusCmd {

    [...] @Autowired dependencies    

    public void run(String[] args) {

        if (args.length != 1) {
            logger.error("Wrong number of arguments");
            return;
        }
        [...]
    }
 }