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 <tadaya@ttddyy.net> See gh-37905
This commit is contained in:
		
							parent
							
								
									27f06ea1e0
								
							
						
					
					
						commit
						8ac597c245
					
				|  | @ -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. | ||||
|  |  | |||
|  | @ -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. | ||||
|  |  | |||
|  | @ -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 { | ||||
| 
 | ||||
| } | ||||
|  | @ -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<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)) { | ||||
| 		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) { | ||||
|  |  | |||
|  | @ -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<String> 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 { | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue