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 194a38940a..4666648570 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 @@ -1455,37 +1455,61 @@ public class SpringApplication { * @param args the main method args * @return the running {@link ApplicationContext} */ - public ConfigurableApplicationContext run(String... args) { - ContextLoaderHook hook = new ContextLoaderHook(this.sources); + public SpringApplication.Running run(String... args) { + RunListener runListener = new RunListener(); + SpringApplicationHook hook = (springApplication) -> { + springApplication.addPrimarySources(this.sources); + return runListener; + }; withHook(hook, () -> this.main.accept(args)); - return hook.applicationContext; + return runListener; } - private static class ContextLoaderHook implements SpringApplicationHook { + /** + * {@link SpringApplicationRunListener} to capture {@link Running} application + * details. + */ + private static class RunListener implements SpringApplicationRunListener, Running { - private final Set> sources; + private final List contexts = Collections + .synchronizedList(new ArrayList<>()); - private ConfigurableApplicationContext applicationContext; - - ContextLoaderHook(Set> sources) { - this.sources = sources; + @Override + public void contextLoaded(ConfigurableApplicationContext context) { + this.contexts.add(context); } @Override - public SpringApplicationRunListener getRunListener(SpringApplication springApplication) { - springApplication.addPrimarySources(this.sources); - return new SpringApplicationRunListener() { - @Override - public void contextPrepared(ConfigurableApplicationContext context) { - ContextLoaderHook.this.applicationContext = context; - } - }; + public ConfigurableApplicationContext getApplicationContext() { + List rootContexts = this.contexts.stream() + .filter((context) -> context.getParent() == null) + .toList(); + Assert.state(!rootContexts.isEmpty(), "No root application context located"); + Assert.state(rootContexts.size() == 1, "No unique root application context located"); + return rootContexts.get(0); } } } + /** + * Provides access to details of a {@link SpringApplication} run using + * {@link Augmented#run(String...)}. + * + * @since 3.1.0 + */ + public interface Running { + + /** + * Return the root {@link ConfigurableApplicationContext} of the running + * application. + * @return the root application context + */ + ConfigurableApplicationContext getApplicationContext(); + + } + /** * {@link BeanFactoryPostProcessor} to re-order our property sources below any * {@code @PropertySource} items added by the {@link ConfigurationClassPostProcessor}. 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 2c9f8a71cb..3b9d87be9b 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 @@ -1372,7 +1372,8 @@ class SpringApplicationTests { void fromReturnsApplicationContext() { ConfigurableApplicationContext context = SpringApplication.from(ExampleFromMainMethod::main) .with(ExampleAdditionalConfig.class) - .run(); + .run() + .getApplicationContext(); assertThat(context).isNotNull(); }