Multiple Spring boot CommandLineRunner based on command line argument

后端 未结 4 962
情话喂你
情话喂你 2020-12-31 16:11

I have created spring boot application with spring cloud task which should executes a few commands(tasks). Each task/command is shorted-lived task, and all tasks are start

相关标签:
4条回答
  • 2020-12-31 16:43

    You can also make your CommandLineRunner implementations @Component and @ConditionalOnExpression("${someproperty:false}")

    then have multiple profiles, that set someproperty to true to include those CommandLineRunners in the Context.

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

    and in the yml application-myrunner.yml

    myRunnerEnabled: true
    
    @SpringBootApplication
    public class SpringMain {
        public static void main(String ... args) {
            SpringApplication.run(SpringMain.class, args);
        }
    }
    
    0 讨论(0)
  • 2020-12-31 16:44

    Similar to this answer https://stackoverflow.com/a/44482525/986160 but using injection and less configuration.

    Having a similar requirement, what has worked for me is to have one CommandLineApps class implementing CommandLineRunner, then inject all my other command line runners as @Component and use the first argument to delegate to one of the injected runners. Please note that the injected ones should not extend CommandLineRunner and not be annotated as @SpringBootAppplication.

    Important: be careful though if you put the call in a crontab one call will destroy the previous one if it is not done.

    Here is an example:

    @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;
            }
            [...]
        }
     }
    
    0 讨论(0)
  • 2020-12-31 16:59

    Spring Boot runs all the CommandLineRunner or ApplicationRunner beans from the application context. You cannot select one by any args.

    So basically you have two possibiities:

    1. You have different CommandLineRunner implementations and in each you check the arguments to determine if this special CommandLineRunner should run.
    2. You implement only one CommandLineRunner which acts as a dispatcher. Code might look something like this:

    This is the new Interface that your runners will implement:

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

    You then define implementations and identify them with a name:

    @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");
        }
    }
    

    and

    @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");
        }
    }
    

    Then in your single CommandLineRunner implementation you get hold of the application context and resolve the required bean by name, my example uses just the first argument, and call it's MyCommandLineRunner.run()method:

    @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;
        }
    }
    
    0 讨论(0)
  • 2020-12-31 17:06

    Strangely there is not a built-in mechanism to select a set of CommandLineRunner. By default all of them are executed.

    I have reused - maybe improperly - the Profile mechanism, that is I have annotated each CommandLineRunner with @org.springframework.context.annotation.Profile("mycommand"), and I select the one I want to execute with the System property -Dspring.profiles.active=mycommand

    For more information on the Profile mechanism, please refer to https://www.baeldung.com/spring-profiles

    0 讨论(0)
提交回复
热议问题