Future proof SpringApplication.from(...) by returning a wrapper type

Update `SpringApplication.run` to that it returns a wrapper which
provides access to the `ApplicationContext` rather than the
`ApplicationContext` directly.

This should provide a natural place to add more accessors in the future
should we need to.

Closes gh-35451
This commit is contained in:
Phillip Webb 2023-05-16 12:55:58 -07:00
parent 5cfc96afbb
commit 595fc13b34
2 changed files with 43 additions and 18 deletions

View File

@ -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<Class<?>> sources;
private final List<ConfigurableApplicationContext> contexts = Collections
.synchronizedList(new ArrayList<>());
private ConfigurableApplicationContext applicationContext;
ContextLoaderHook(Set<Class<?>> 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<ConfigurableApplicationContext> 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}.

View File

@ -1372,7 +1372,8 @@ class SpringApplicationTests {
void fromReturnsApplicationContext() {
ConfigurableApplicationContext context = SpringApplication.from(ExampleFromMainMethod::main)
.with(ExampleAdditionalConfig.class)
.run();
.run()
.getApplicationContext();
assertThat(context).isNotNull();
}