Apply additional sources once when using SpringApplication.from()
Previously, when using SpringApplication.from() any additional sources configured using with() would be applied to every SpringApplication that was created within the scope of the call to run(). This caused problems with Spring Cloud's bootstrap context where the additional sources would be applied to both the user's application and to the boostrap context's application. This commit updates the hook that's used to apply the additional sources so that it's only applied once. This results in the additional sources only being added to the first SpringApplication that is run. Closes gh-35873
This commit is contained in:
@@ -30,6 +30,7 @@ import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
@@ -1457,10 +1458,10 @@ public class SpringApplication {
|
||||
*/
|
||||
public SpringApplication.Running run(String... args) {
|
||||
RunListener runListener = new RunListener();
|
||||
SpringApplicationHook hook = (springApplication) -> {
|
||||
SpringApplicationHook hook = new SingleUseSpringApplicationHook((springApplication) -> {
|
||||
springApplication.addPrimarySources(this.sources);
|
||||
return runListener;
|
||||
};
|
||||
});
|
||||
withHook(hook, () -> this.main.accept(args));
|
||||
return runListener;
|
||||
}
|
||||
@@ -1580,4 +1581,21 @@ public class SpringApplication {
|
||||
|
||||
}
|
||||
|
||||
private static final class SingleUseSpringApplicationHook implements SpringApplicationHook {
|
||||
|
||||
private final AtomicBoolean used = new AtomicBoolean();
|
||||
|
||||
private final SpringApplicationHook delegate;
|
||||
|
||||
private SingleUseSpringApplicationHook(SpringApplicationHook delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpringApplicationRunListener getRunListener(SpringApplication springApplication) {
|
||||
return this.used.compareAndSet(false, true) ? this.delegate.getRunListener(springApplication) : null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Supplier;
|
||||
@@ -1363,18 +1364,30 @@ class SpringApplicationTests {
|
||||
@Test
|
||||
void fromRunsWithAdditionalSources() {
|
||||
assertThat(ExampleAdditionalConfig.local.get()).isNull();
|
||||
SpringApplication.from(ExampleFromMainMethod::main).with(ExampleAdditionalConfig.class).run();
|
||||
this.context = SpringApplication.from(ExampleFromMainMethod::main)
|
||||
.with(ExampleAdditionalConfig.class)
|
||||
.run()
|
||||
.getApplicationContext();
|
||||
assertThat(ExampleAdditionalConfig.local.get()).isNotNull();
|
||||
ExampleAdditionalConfig.local.set(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromReturnsApplicationContext() {
|
||||
ConfigurableApplicationContext context = SpringApplication.from(ExampleFromMainMethod::main)
|
||||
this.context = SpringApplication.from(ExampleFromMainMethod::main)
|
||||
.with(ExampleAdditionalConfig.class)
|
||||
.run()
|
||||
.getApplicationContext();
|
||||
assertThat(context).isNotNull();
|
||||
assertThat(this.context).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromWithMultipleApplicationsOnlyAppliesAdditionalSourcesOnce() {
|
||||
this.context = SpringApplication.from(MultipleApplicationsMainMethod::main)
|
||||
.with(SingleUseAdditionalConfig.class)
|
||||
.run()
|
||||
.getApplicationContext();
|
||||
assertThatNoException().isThrownBy(() -> this.context.getBean(SingleUseAdditionalConfig.class));
|
||||
}
|
||||
|
||||
private <S extends AvailabilityState> ArgumentMatcher<ApplicationEvent> isAvailabilityChangeEventWithState(
|
||||
@@ -1949,6 +1962,31 @@ class SpringApplicationTests {
|
||||
|
||||
}
|
||||
|
||||
static class MultipleApplicationsMainMethod {
|
||||
|
||||
static void main(String[] args) {
|
||||
SpringApplication application = new SpringApplication(ExampleConfig.class);
|
||||
application.setWebApplicationType(WebApplicationType.NONE);
|
||||
application.addListeners(new ApplicationListener<ApplicationEnvironmentPreparedEvent>() {
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
|
||||
SpringApplicationBuilder builder = new SpringApplicationBuilder(
|
||||
InnerApplicationConfiguration.class);
|
||||
builder.web(WebApplicationType.NONE);
|
||||
builder.run().close();
|
||||
}
|
||||
|
||||
});
|
||||
application.run(args);
|
||||
}
|
||||
|
||||
static class InnerApplicationConfiguration {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ExampleAdditionalConfig {
|
||||
|
||||
@@ -1960,4 +1998,17 @@ class SpringApplicationTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class SingleUseAdditionalConfig {
|
||||
|
||||
private static AtomicBoolean used = new AtomicBoolean(false);
|
||||
|
||||
SingleUseAdditionalConfig() {
|
||||
if (!used.compareAndSet(false, true)) {
|
||||
throw new IllegalStateException("Single-use configuration has already been used");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user