From 8ac597c245956ca6e584e2504a8b9418f876ba43 Mon Sep 17 00:00:00 2001 From: Tadaya Tsuyukubo Date: Mon, 16 Oct 2023 21:10:47 -0700 Subject: [PATCH] Support @Order on [CommandLine|Application]Runner @Bean definitions Prior to this commit, `@Order` annotation on `@Bean` method was not considered for `CommandLineRunner` and `ApplicationRunner`. This commit introduces a `Runner` marker interface and uses it to retrieve the runner beans. As a result, it enables the use of `@Order` annotations on `@Bean` methods for both `CommandLineRunner` and `ApplicationRunner`. Signed-off-by: Tadaya Tsuyukubo See gh-37905 --- .../boot/ApplicationRunner.java | 2 +- .../boot/CommandLineRunner.java | 2 +- .../java/org/springframework/boot/Runner.java | 28 +++++++++++++++ .../boot/SpringApplication.java | 9 ++--- .../boot/SpringApplicationTests.java | 36 +++++++++++++++++++ 5 files changed, 69 insertions(+), 8 deletions(-) create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/Runner.java diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ApplicationRunner.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ApplicationRunner.java index 679d1600cf6..08175746086 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ApplicationRunner.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ApplicationRunner.java @@ -30,7 +30,7 @@ import org.springframework.core.annotation.Order; * @see CommandLineRunner */ @FunctionalInterface -public interface ApplicationRunner { +public interface ApplicationRunner extends Runner { /** * Callback used to run the bean. diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/CommandLineRunner.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/CommandLineRunner.java index f6765fbf1b3..855dd6d29a2 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/CommandLineRunner.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/CommandLineRunner.java @@ -33,7 +33,7 @@ import org.springframework.core.annotation.Order; * @see ApplicationRunner */ @FunctionalInterface -public interface CommandLineRunner { +public interface CommandLineRunner extends Runner { /** * Callback used to run the bean. diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/Runner.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/Runner.java new file mode 100644 index 00000000000..5c0c2edd127 --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/Runner.java @@ -0,0 +1,28 @@ +/* + * Copyright 2012-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot; + +/** + * Marker interface for runners. + * + * @author Tadaya Tsuyukubo + * @see ApplicationRunner + * @see CommandLineRunner + */ +interface Runner { + +} diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java index 7acd007a027..4ad0a6bd082 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java @@ -156,6 +156,7 @@ import org.springframework.util.StringUtils; * @author Brian Clozel * @author Ethan Rubinson * @author Chris Bono + * @author Tadaya Tsuyukubo * @since 1.0.0 * @see #run(Class, String[]) * @see #run(Class[], String[]) @@ -740,18 +741,14 @@ public class SpringApplication { } private void callRunners(ApplicationContext context, ApplicationArguments args) { - List 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)) { + context.getBeanProvider(Runner.class).orderedStream().forEach((runner) -> { if (runner instanceof ApplicationRunner) { callRunner((ApplicationRunner) runner, args); } if (runner instanceof CommandLineRunner) { callRunner((CommandLineRunner) runner, args); } - } + }); } private void callRunner(ApplicationRunner runner, ApplicationArguments args) { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java index 72ffcc9f428..d31419c0c39 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java @@ -95,6 +95,7 @@ import org.springframework.context.event.SmartApplicationListener; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.StaticApplicationContext; import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; import org.springframework.core.env.CommandLinePropertySource; import org.springframework.core.env.CompositePropertySource; import org.springframework.core.env.ConfigurableEnvironment; @@ -154,6 +155,7 @@ import static org.mockito.Mockito.spy; * @author Nguyen Bao Sach * @author Chris Bono * @author Brian Clozel + * @author Tadaya Tsuyukubo */ @ExtendWith(OutputCaptureExtension.class) class SpringApplicationTests { @@ -667,6 +669,15 @@ class SpringApplicationTests { assertThat(this.context).has(runTestRunnerBean("runnerC")); } + @Test + void runFunctionalCommandLineRunnersAndApplicationRunners() { + SpringApplication application = new SpringApplication(FunctionalRunnerConfig.class); + application.setWebApplicationType(WebApplicationType.NONE); + this.context = application.run("arg"); + FunctionalRunnerConfig config = this.context.getBean(FunctionalRunnerConfig.class); + assertThat(config.runners).containsExactly("runnerA", "runnerB", "runnerC"); + } + @Test @SuppressWarnings("unchecked") void runnersAreCalledAfterStartedIsLoggedAndBeforeApplicationReadyEventIsPublished(CapturedOutput output) @@ -1573,6 +1584,31 @@ class SpringApplicationTests { } + @Configuration(proxyBeanMethods = false) + static class FunctionalRunnerConfig { + + List runners = new ArrayList<>(); + + @Bean + @Order // default is LOWEST_PRECEDENCE + CommandLineRunner runnerC() { + return (args) -> this.runners.add("runnerC"); + } + + @Bean + @Order(Ordered.LOWEST_PRECEDENCE - 1) + ApplicationRunner runnerB() { + return (args) -> this.runners.add("runnerB"); + } + + @Bean + @Order(Ordered.HIGHEST_PRECEDENCE) + CommandLineRunner runnerA() { + return (args) -> this.runners.add("runnerA"); + } + + } + @Configuration(proxyBeanMethods = false) static class ExitCodeCommandLineRunConfig {