Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ApplicationRunner VS CommandLineRunner

I am new to Spring boot and read about the ApplicationRunner and CommandLineRunner. Both are functioning same. When I implement both interfaces at the same time, always CommandLineRunner's method runs first then ApplicationRunner's method.

Anyone can help, why CommandLineRunner's method getting preference over ApplicationRunner's method.

like image 359
JavaFun Avatar asked Oct 26 '25 06:10

JavaFun


2 Answers

Here's a real answer to your question, obtained by looking at the Spring code. All runners are run by the main thread at the end of startup, via this method:

private void callRunners(ApplicationContext context, ApplicationArguments args) {
    List<Object> runners = new ArrayList<>();
    runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
    runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
    AnnotationAwareOrderComparator.sort(runners);
    for (Object runner : new LinkedHashSet<>(runners)) {
        if (runner instanceof ApplicationRunner) {
            callRunner((ApplicationRunner) runner, args);
        }
        if (runner instanceof CommandLineRunner) {
            callRunner((CommandLineRunner) runner, args);
        }
    }
}

It sounded to me like what you were doing was implementing both runner interfaces on the same object. When you do that, the above code shows that the ApplicationRunner method is run first, and then the CommandLineRunner method. But you seem to say that you see the opposite behavior. I implemented a single class that implemented both runners, and I saw the two methods executed in the order I expected per the above code.

So you must be implementing these interfaces in two different classes. In that case, the order is determined by how the AnnotationAwareOrderComparator.sort method chooses to order your two runner classes, because this method is used, per the above code, to sort the list of all runners to determine the order in which they are called.

The description of the AnnotationAwareOrderComparator is:

AnnotationAwareOrderComparator is an extension of OrderComparator that supports Spring's Ordered interface as well as the @Order and @Priority annotations, with an order value provided by an Ordered instance overriding a statically defined annotation value (if any).

And the sort method's description is:

Sort the given list with a default AnnotationAwareOrderComparator.

So apparently, if you wanted to force a particular order of execution of your runners, you could use @order or @Priority annotations to accomplish that.

So there you have it. If you need more detail than this, you'll need to dig into the details of how the AnnotationAwareOrderComparator chooses how to order your two classes.

like image 117
CryptoFool Avatar answered Oct 28 '25 19:10

CryptoFool


Well, I don't think it's needed in any case to implement both of these interfaces at the same time.

Both, when implemented, indicate that the run method should be invoked at the application startup.

The difference between ApplicationRunner and CommandLineRunner is that, on ApplicationRunner, instead of raw string arguments passed to the run method, we have an instance of ApplicationArguments so that you can access the bootstrap arguments passed when initializing the application.

With CommandLineRunner you can access them as well, but as raw string arguments, so you'll have to parse them yourself.

You could test it yourself:

Running the application with the argument --my-config=xyz will provide the bellow result when implementing ApplicationRunner or CommandLineRunner:

Using CommandLineRunner:

@Component
public class CLIRunner implements CommandLineRunner {

    @Override
    public void run(String...args) throws Exception {
        System.out.println("Arguments passed when bootstraping the app: " + Arrays.asList(args));
    }
}

Output:

Arguments passed when bootstraping the app: [--my-config=xyz]

Using ApplicationRunner:

@Component
public class AppRunner implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("Arguments passed when bootstraping the app: " + args.getOptionNames());
    }
}

Output:

Arguments passed when bootstraping the app: [my-config]

As you saw, both of them provide, almost, the same functionality. I would suggest always to use ApplicationRunner, because then you don't have to parse the arguments yourself, since Spring already did and made it available to you within the ApplicationArguments object.

like image 38
Matheus Cirillo Avatar answered Oct 28 '25 21:10

Matheus Cirillo



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!