Sorting CommandLineRunner execution

Viewed 974

I've four CommandLineRunner to be executed in a specific order. Two of them are annotated as @Component and the other two are declared as Bean. I used the @Order annotation to specify the execution order but I found an unexpected behavior running the code below:

...
@SpringBootApplication
public class App {

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }


    @Bean
    @Order(2)
    public CommandLineRunner upRunner(){
        return args -> log.info("2 - Running upRunner");
    }

    @Bean
    @Order(1)
    public CommandLineRunner downRunner(){
        return args -> log.info("1 - Running downRunner");
    }
}
...
@Component
@Order(4)
public class RightRunner implements CommandLineRunner {

    @Override
    public void run(String... args) throws Exception {
        log.info("4 - Running RightRunner");
    }
}
...
@Component
@Order(3)
public class LeftRunner implements CommandLineRunner {

    @Override
    public void run(String... args) throws Exception {
        log.info("3 - Running LeftRunner");
    }
}

The execution does not follow the order specified by the @Order annotation, see the log below:

[INFO ] Started App in 2.988 seconds (JVM running for 3.949)
[INFO ] 3 - Running LeftRunner
[INFO ] 4 - Running RightRunner
[INFO ] 2 - Running upRunner
[INFO ] 1 - Running downRunner

So, it seems that:

  • the runners annotated as @Component are executed in the expected order and before the others.
  • the runners declared as @Bean are executed in the order they are defined in the App class with no regard of the @Order annotation.

Is there something I miss to enforce the required order? Could someone clarify this behavior, I'm using spring-boot version 2.4.0?

It is also quite strange that Spring is able to properly sort those beans when injected, just adding the Bean below:

...

    @Bean
    public Object runners(List<CommandLineRunner> runners) throws Exception {

        for(int i=0; i<runners.size(); i++){
            runners.get(i).run(null);
        }
        return new Object();
    }

the log shows that the injected CommandLineRunner list is sorted according to the @Order annotation:

[INFO ] 1 - Running downRunner
[INFO ] 2 - Running upRunner
[INFO ] 3 - Running LeftRunner
[INFO ] 4 - Running RightRunner
[INFO ] ...
[INFO ] Started App in 2.754 seconds (JVM running for 3.847)
[INFO ] 3 - Running LeftRunner
[INFO ] 4 - Running RightRunner
[INFO ] 2 - Running upRunner
[INFO ] 1 - Running downRunner

Thanks in advance,

Andrea

2 Answers

I've been debugging and I see that in AnnotationAwareOrderComparator, which is used for ordering CommandLineRunners, when it gets to method findOrderFromAnnotation it uses OrderUtils.getOrderFromAnnotations(element, annotations) to obtain the order specified. This method is returning null in your case for the beans declared as @Bean and not for the ones declared as @Component.

I am sorry I couldn't get further and I cannot make sense of this behaviour either.

I also faced the same issue.
Since I could not resolve it with @Order interface, I implemented Ordered interface to fix the issue

Related