Skip to content

Commit b08d441

Browse files
committed
Merge branch '3.1.x'
Closes gh-38837
2 parents f2a74c9 + 13fb450 commit b08d441

File tree

2 files changed

+113
-29
lines changed

2 files changed

+113
-29
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java

Lines changed: 75 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,15 @@
1818

1919
import java.lang.StackWalker.StackFrame;
2020
import java.lang.management.ManagementFactory;
21+
import java.lang.reflect.Method;
2122
import java.time.Duration;
2223
import java.util.ArrayList;
2324
import java.util.Arrays;
2425
import java.util.Collection;
2526
import java.util.Collections;
27+
import java.util.Comparator;
2628
import java.util.HashMap;
29+
import java.util.IdentityHashMap;
2730
import java.util.LinkedHashSet;
2831
import java.util.List;
2932
import java.util.Map;
@@ -41,14 +44,17 @@
4144

4245
import org.springframework.aot.AotDetector;
4346
import org.springframework.beans.BeansException;
47+
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
4448
import org.springframework.beans.factory.config.BeanDefinition;
4549
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
50+
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
4651
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
4752
import org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader;
4853
import org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory;
4954
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
5055
import org.springframework.beans.factory.support.BeanNameGenerator;
5156
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
57+
import org.springframework.beans.factory.support.RootBeanDefinition;
5258
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
5359
import org.springframework.boot.Banner.Mode;
5460
import org.springframework.boot.context.properties.bind.Bindable;
@@ -74,6 +80,8 @@
7480
import org.springframework.context.support.AbstractApplicationContext;
7581
import org.springframework.context.support.GenericApplicationContext;
7682
import org.springframework.core.GenericTypeResolver;
83+
import org.springframework.core.OrderComparator;
84+
import org.springframework.core.OrderComparator.OrderSourceProvider;
7785
import org.springframework.core.Ordered;
7886
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
7987
import org.springframework.core.annotation.Order;
@@ -762,35 +770,42 @@ protected void refresh(ConfigurableApplicationContext applicationContext) {
762770
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
763771
}
764772

765-
private void callRunners(ApplicationContext context, ApplicationArguments args) {
766-
context.getBeanProvider(Runner.class).orderedStream().forEach((runner) -> {
767-
if (runner instanceof ApplicationRunner applicationRunner) {
768-
callRunner(applicationRunner, args);
769-
}
770-
if (runner instanceof CommandLineRunner commandLineRunner) {
771-
callRunner(commandLineRunner, args);
772-
}
773-
});
773+
private void callRunners(ConfigurableApplicationContext context, ApplicationArguments args) {
774+
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
775+
String[] beanNames = beanFactory.getBeanNamesForType(Runner.class);
776+
Map<Runner, String> instancesToBeanNames = new IdentityHashMap<>();
777+
for (String beanName : beanNames) {
778+
instancesToBeanNames.put(beanFactory.getBean(beanName, Runner.class), beanName);
779+
}
780+
Comparator<Object> comparator = getOrderComparator(beanFactory)
781+
.withSourceProvider(new FactoryAwareOrderSourceProvider(beanFactory, instancesToBeanNames));
782+
instancesToBeanNames.keySet().stream().sorted(comparator).forEach((runner) -> callRunner(runner, args));
774783
}
775784

776-
private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
777-
try {
778-
(runner).run(args);
779-
}
780-
catch (Exception ex) {
781-
throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
782-
}
785+
private OrderComparator getOrderComparator(ConfigurableListableBeanFactory beanFactory) {
786+
Comparator<?> dependencyComparator = (beanFactory instanceof DefaultListableBeanFactory defaultListableBeanFactory)
787+
? defaultListableBeanFactory.getDependencyComparator() : null;
788+
return (dependencyComparator instanceof OrderComparator orderComparator) ? orderComparator
789+
: AnnotationAwareOrderComparator.INSTANCE;
783790
}
784791

785-
private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
786-
try {
787-
(runner).run(args.getSourceArgs());
792+
private void callRunner(Runner runner, ApplicationArguments args) {
793+
if (runner instanceof ApplicationRunner) {
794+
callRunner(ApplicationRunner.class, runner, (applicationRunner) -> applicationRunner.run(args));
788795
}
789-
catch (Exception ex) {
790-
throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
796+
if (runner instanceof CommandLineRunner) {
797+
callRunner(CommandLineRunner.class, runner,
798+
(commandLineRunner) -> commandLineRunner.run(args.getSourceArgs()));
791799
}
792800
}
793801

802+
@SuppressWarnings("unchecked")
803+
private <R extends Runner> void callRunner(Class<R> type, Runner runner, ThrowingConsumer<R> call) {
804+
call.throwing(
805+
(message, ex) -> new IllegalStateException("Failed to execute " + ClassUtils.getShortName(type), ex))
806+
.accept((R) runner);
807+
}
808+
794809
private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception,
795810
SpringApplicationRunListeners listeners) {
796811
try {
@@ -1636,8 +1651,8 @@ public SpringApplicationRunListener getRunListener(SpringApplication springAppli
16361651
}
16371652

16381653
/**
1639-
* Starts a non-daemon thread to keep the JVM alive on {@link ContextRefreshedEvent}.
1640-
* Stops the thread on {@link ContextClosedEvent}.
1654+
* <<<<<<< HEAD Starts a non-daemon thread to keep the JVM alive on
1655+
* {@link ContextRefreshedEvent}. Stops the thread on {@link ContextClosedEvent}.
16411656
*/
16421657
private static final class KeepAlive implements ApplicationListener<ApplicationContextEvent> {
16431658

@@ -1774,4 +1789,41 @@ String action() {
17741789

17751790
}
17761791

1792+
/**
1793+
* {@link OrderSourceProvider} used to obtain factory method and target type order
1794+
* sources. Based on internal {@link DefaultListableBeanFactory} code.
1795+
*/
1796+
private class FactoryAwareOrderSourceProvider implements OrderSourceProvider {
1797+
1798+
private final ConfigurableBeanFactory beanFactory;
1799+
1800+
private final Map<?, String> instancesToBeanNames;
1801+
1802+
FactoryAwareOrderSourceProvider(ConfigurableBeanFactory beanFactory, Map<?, String> instancesToBeanNames) {
1803+
this.beanFactory = beanFactory;
1804+
this.instancesToBeanNames = instancesToBeanNames;
1805+
}
1806+
1807+
@Override
1808+
public Object getOrderSource(Object obj) {
1809+
String beanName = this.instancesToBeanNames.get(obj);
1810+
return (beanName != null) ? getOrderSource(beanName, obj.getClass()) : null;
1811+
}
1812+
1813+
private Object getOrderSource(String beanName, Class<?> instanceType) {
1814+
try {
1815+
RootBeanDefinition beanDefinition = (RootBeanDefinition) this.beanFactory
1816+
.getMergedBeanDefinition(beanName);
1817+
Method factoryMethod = beanDefinition.getResolvedFactoryMethod();
1818+
Class<?> targetType = beanDefinition.getTargetType();
1819+
targetType = (targetType != instanceType) ? targetType : null;
1820+
return Stream.of(factoryMethod, targetType).filter(Objects::nonNull).toArray();
1821+
}
1822+
catch (NoSuchBeanDefinitionException ex) {
1823+
return null;
1824+
}
1825+
}
1826+
1827+
}
1828+
17771829
}

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
import org.springframework.boot.availability.AvailabilityState;
6363
import org.springframework.boot.availability.LivenessState;
6464
import org.springframework.boot.availability.ReadinessState;
65+
import org.springframework.boot.builder.ParentContextApplicationContextInitializer;
6566
import org.springframework.boot.builder.SpringApplicationBuilder;
6667
import org.springframework.boot.context.event.ApplicationContextInitializedEvent;
6768
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
@@ -634,6 +635,19 @@ void runCommandLineRunnersAndApplicationRunners() {
634635
assertThat(this.context).has(runTestRunnerBean("runnerC"));
635636
}
636637

638+
@Test
639+
void runCommandLineRunnersAndApplicationRunnersWithParentContext() {
640+
SpringApplication application = new SpringApplication(CommandLineRunConfig.class);
641+
application.setWebApplicationType(WebApplicationType.NONE);
642+
application.addInitializers(new ParentContextApplicationContextInitializer(
643+
new AnnotationConfigApplicationContext(CommandLineRunParentConfig.class)));
644+
this.context = application.run("arg");
645+
assertThat(this.context).has(runTestRunnerBean("runnerA"));
646+
assertThat(this.context).has(runTestRunnerBean("runnerB"));
647+
assertThat(this.context).has(runTestRunnerBean("runnerC"));
648+
assertThat(this.context).doesNotHave(runTestRunnerBean("runnerP"));
649+
}
650+
637651
@Test
638652
void runCommandLineRunnersAndApplicationRunnersUsingOrderOnBeanDefinitions() {
639653
SpringApplication application = new SpringApplication(BeanDefinitionOrderRunnerConfig.class);
@@ -1477,7 +1491,7 @@ public boolean matches(ConfigurableEnvironment value) {
14771491
};
14781492
}
14791493

1480-
private Condition<ConfigurableApplicationContext> runTestRunnerBean(final String name) {
1494+
private Condition<ConfigurableApplicationContext> runTestRunnerBean(String name) {
14811495
return new Condition<>("run testrunner bean") {
14821496

14831497
@Override
@@ -1691,17 +1705,27 @@ static class CommandLineRunConfig {
16911705

16921706
@Bean
16931707
TestCommandLineRunner runnerC() {
1694-
return new TestCommandLineRunner(Ordered.LOWEST_PRECEDENCE, "runnerB", "runnerA");
1708+
return new TestCommandLineRunner("runnerC", Ordered.LOWEST_PRECEDENCE, "runnerB", "runnerA");
16951709
}
16961710

16971711
@Bean
16981712
TestApplicationRunner runnerB() {
1699-
return new TestApplicationRunner(Ordered.LOWEST_PRECEDENCE - 1, "runnerA");
1713+
return new TestApplicationRunner("runnerB", Ordered.LOWEST_PRECEDENCE - 1, "runnerA");
17001714
}
17011715

17021716
@Bean
17031717
TestCommandLineRunner runnerA() {
1704-
return new TestCommandLineRunner(Ordered.HIGHEST_PRECEDENCE);
1718+
return new TestCommandLineRunner("runnerA", Ordered.HIGHEST_PRECEDENCE);
1719+
}
1720+
1721+
}
1722+
1723+
@Configuration(proxyBeanMethods = false)
1724+
static class CommandLineRunParentConfig {
1725+
1726+
@Bean
1727+
TestCommandLineRunner runnerP() {
1728+
return new TestCommandLineRunner("runnerP", Ordered.LOWEST_PRECEDENCE);
17051729
}
17061730

17071731
}
@@ -1910,25 +1934,33 @@ boolean hasRun() {
19101934

19111935
static class TestCommandLineRunner extends AbstractTestRunner implements CommandLineRunner {
19121936

1913-
TestCommandLineRunner(int order, String... expectedBefore) {
1937+
private final String name;
1938+
1939+
TestCommandLineRunner(String name, int order, String... expectedBefore) {
19141940
super(order, expectedBefore);
1941+
this.name = name;
19151942
}
19161943

19171944
@Override
19181945
public void run(String... args) {
1946+
System.out.println(">>> " + this.name);
19191947
markAsRan();
19201948
}
19211949

19221950
}
19231951

19241952
static class TestApplicationRunner extends AbstractTestRunner implements ApplicationRunner {
19251953

1926-
TestApplicationRunner(int order, String... expectedBefore) {
1954+
private final String name;
1955+
1956+
TestApplicationRunner(String name, int order, String... expectedBefore) {
19271957
super(order, expectedBefore);
1958+
this.name = name;
19281959
}
19291960

19301961
@Override
19311962
public void run(ApplicationArguments args) {
1963+
System.out.println(">>> " + this.name);
19321964
markAsRan();
19331965
}
19341966

0 commit comments

Comments
 (0)