11

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

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

4

4 回答 4

10

您还可以使您的 CommandLineRunner 实现 @Component 和 @ConditionalOnExpression("${someproperty:false}")

然后有多个配置文件,将 someproperty 设置为 true 以将这些 CommandLineRunners 包含在上下文中。

@Component
@Slf4j
@ConditionalOnExpression("${myRunnerEnabled:false}")
public class MyRunner implements CommandLineRunner {
    @Override
    public void run(String ... args) throws Exception {
        log.info("this ran");
    }
}

并在 yml application-myrunner.yml

myRunnerEnabled: true
@SpringBootApplication
public class SpringMain {
    public static void main(String ... args) {
        SpringApplication.run(SpringMain.class, args);
    }
}
于 2019-03-12T17:35:26.403 回答
7

Spring Boot从应用程序上下文运行所有CommandLineRunner或bean。ApplicationRunner您不能通过任何参数选择一个。

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

  1. 您有不同的CommandLineRunner实现,并在每个实现中检查参数以确定是否CommandLineRunner应该运行此特殊功能。
  2. 您只实现一个CommandLineRunner充当调度程序的角色。代码可能如下所示:

这是您的跑步者将实现的新界面:

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;
    }
}
于 2017-06-11T09:58:38.017 回答
2

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

我已经重用了——也许是不正确的——Profile 机制,也就是说我用@org.springframework.context.annotation.Profile("mycommand") 注释了每个CommandLineRunner,然后我选择了我想用系统属性-Dspring 执行的那个.profiles.active=我的命令

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

于 2019-03-30T16:32:13.247 回答
1

类似于这个答案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;
        }
        [...]
    }
 }
于 2019-11-09T09:25:43.347 回答