我已经使用spring cloud任务创建了spring boot应用程序,它应该执行一些命令(任务)。 每个任务/命令都是短期任务,所有任务都从命令行开始,执行一些简短的ETL作业并完成执行。
有一个弹簧启动jar包含所有命令/任务。 每个任务都是CommandLineRunner,我喜欢根据命令行中的参数来决定执行哪些任务(一个或多个)。 这样做的最佳做法是什么? 我不喜欢有脏代码询问“if else”或类似的东西。
答案 0 :(得分:3)
Spring Boot从应用程序上下文运行所有CommandLineRunner
或ApplicationRunner
bean。你不能选择任何一个args。
所以基本上你有两种可能性:
CommandLineRunner
实现,并在每个实现中检查参数以确定是否应运行此特殊CommandLineRunner
。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;
}
}
答案 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;
}
[...]
}
}