diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfigurationTests.java
index 7f854d2c529..b4596651e95 100644
--- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfigurationTests.java
+++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfigurationTests.java
@@ -37,6 +37,7 @@ import org.junit.jupiter.api.io.TempDir;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.boot.DefaultBootstrapContext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
@@ -78,8 +79,8 @@ class LiquibaseAutoConfigurationTests {
@BeforeEach
void init() {
- new LiquibaseServiceLocatorApplicationListener()
- .onApplicationEvent(new ApplicationStartingEvent(new SpringApplication(Object.class), new String[0]));
+ new LiquibaseServiceLocatorApplicationListener().onApplicationEvent(new ApplicationStartingEvent(
+ new DefaultBootstrapContext(), new SpringApplication(Object.class), new String[0]));
}
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
diff --git a/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/RestartApplicationListenerTests.java b/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/RestartApplicationListenerTests.java
index 05bf343d31d..923781c9a69 100644
--- a/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/RestartApplicationListenerTests.java
+++ b/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/RestartApplicationListenerTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2019 the original author or authors.
+ * Copyright 2012-2020 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.
@@ -23,6 +23,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.boot.DefaultBootstrapContext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.event.ApplicationFailedEvent;
import org.springframework.boot.context.event.ApplicationPreparedEvent;
@@ -90,9 +91,10 @@ class RestartApplicationListenerTests {
private void testInitialize(boolean failed) {
Restarter.clearInstance();
RestartApplicationListener listener = new RestartApplicationListener();
+ DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
SpringApplication application = new SpringApplication();
ConfigurableApplicationContext context = mock(ConfigurableApplicationContext.class);
- listener.onApplicationEvent(new ApplicationStartingEvent(application, ARGS));
+ listener.onApplicationEvent(new ApplicationStartingEvent(bootstrapContext, application, ARGS));
assertThat(Restarter.getInstance()).isNotEqualTo(nullValue());
assertThat(Restarter.getInstance().isFinished()).isFalse();
listener.onApplicationEvent(new ApplicationPreparedEvent(application, ARGS, context));
diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/ConfigDataApplicationContextInitializer.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/ConfigDataApplicationContextInitializer.java
index 935cd6f1cab..a7b0adbe4b7 100644
--- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/ConfigDataApplicationContextInitializer.java
+++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/ConfigDataApplicationContextInitializer.java
@@ -16,10 +16,10 @@
package org.springframework.boot.test.context;
+import org.springframework.boot.DefaultBootstrapContext;
import org.springframework.boot.DefaultPropertiesPropertySource;
import org.springframework.boot.context.config.ConfigData;
import org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor;
-import org.springframework.boot.env.DefaultBootstrapRegisty;
import org.springframework.boot.env.RandomValuePropertySource;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
@@ -42,9 +42,9 @@ public class ConfigDataApplicationContextInitializer
public void initialize(ConfigurableApplicationContext applicationContext) {
ConfigurableEnvironment environment = applicationContext.getEnvironment();
RandomValuePropertySource.addToEnvironment(environment);
- DefaultBootstrapRegisty bootstrapRegistry = new DefaultBootstrapRegisty();
- ConfigDataEnvironmentPostProcessor.applyTo(environment, applicationContext, bootstrapRegistry);
- bootstrapRegistry.applicationContextPrepared(applicationContext);
+ DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
+ ConfigDataEnvironmentPostProcessor.applyTo(environment, applicationContext, bootstrapContext);
+ bootstrapContext.close(applicationContext);
DefaultPropertiesPropertySource.moveToEnd(environment);
}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/BootstrapContext.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/BootstrapContext.java
new file mode 100644
index 00000000000..4155ec535a7
--- /dev/null
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/BootstrapContext.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2012-2020 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;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.core.env.Environment;
+
+/**
+ * A simple bootstrap context that is available during startup and {@link Environment}
+ * post-processing up to the point that the {@link ApplicationContext} is prepared.
+ *
+ * Provides lazy access to singletons that may be expensive to create, or need to be
+ * shared before the {@link ApplicationContext} is available.
+ *
+ * @author Phillip Webb
+ * @since 2.4.0
+ */
+public interface BootstrapContext {
+
+ /**
+ * Return an instance from the context, creating it if it hasn't been accessed
+ * previously.
+ * @param the instance type
+ * @param type the instance type
+ * @return the instance managed by the context
+ * @throws IllegalStateException if the type has not been registered
+ */
+ T get(Class type) throws IllegalStateException;
+
+}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/BootstrapContextClosedEvent.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/BootstrapContextClosedEvent.java
new file mode 100644
index 00000000000..2be023e4731
--- /dev/null
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/BootstrapContextClosedEvent.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2012-2020 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;
+
+import org.springframework.context.ApplicationEvent;
+import org.springframework.context.ConfigurableApplicationContext;
+
+/**
+ * {@link ApplicationEvent} published by a {@link BootstrapContext} when it's closed.
+ *
+ * @author Phillip Webb
+ * @since 2.4.0
+ * @see BootstrapRegistry#addCloseListener(org.springframework.context.ApplicationListener)
+ */
+public class BootstrapContextClosedEvent extends ApplicationEvent {
+
+ private final ConfigurableApplicationContext applicationContext;
+
+ BootstrapContextClosedEvent(BootstrapContext source, ConfigurableApplicationContext applicationContext) {
+ super(source);
+ this.applicationContext = applicationContext;
+ }
+
+ /**
+ * Return the {@link BootstrapContext} that was closed.
+ * @return the bootstrap context
+ */
+ public BootstrapContext getBootstrapContext() {
+ return (BootstrapContext) this.source;
+ }
+
+ /**
+ * Return the prepared application context.
+ * @return the application context
+ */
+ public ConfigurableApplicationContext getApplicationContext() {
+ return this.applicationContext;
+ }
+
+}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/BootstrapRegistry.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/BootstrapRegistry.java
new file mode 100644
index 00000000000..940986800d8
--- /dev/null
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/BootstrapRegistry.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2012-2020 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;
+
+import java.util.function.Supplier;
+
+import io.undertow.servlet.api.InstanceFactory;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationListener;
+import org.springframework.core.env.Environment;
+
+/**
+ * A simple object registry that is available during startup and {@link Environment}
+ * post-processing up to the point that the {@link ApplicationContext} is prepared.
+ *
+ * Can be used to register instances that may be expensive to create, or need to be shared
+ * before the {@link ApplicationContext} is available.
+ *
+ * The registry uses {@link Class} as a key, meaning that only a single instance of a
+ * given type can be stored.
+ *
+ * The {@link #addCloseListener(ApplicationListener)} method can be used to add a listener
+ * that can perform actions when {@link BootstrapContext} has been closed and the
+ * {@link ApplicationContext} is fully prepared. For example, an instance may choose to
+ * register itself as a regular Spring bean so that it is available for the application to
+ * use.
+ *
+ * @author Phillip Webb
+ * @since 2.4.0
+ * @see BootstrapContext
+ * @see ConfigurableBootstrapContext
+ */
+public interface BootstrapRegistry {
+
+ /**
+ * Register a specific type with the registry. If the specified type has already been
+ * registered, but not get obtained, it will be replaced.
+ * @param the instance type
+ * @param type the instance type
+ * @param instanceSupplier the instance supplier
+ */
+ void register(Class type, InstanceSupplier instanceSupplier);
+
+ /**
+ * Register a specific type with the registry if one is not already present.
+ * @param the instance type
+ * @param type the instance type
+ * @param instanceSupplier the instance supplier
+ */
+ void registerIfAbsent(Class type, InstanceSupplier instanceSupplier);
+
+ /**
+ * Return if a registration exists for the given type.
+ * @param the instance type
+ * @param type the instance type
+ * @return {@code true} if the type has already been registered
+ */
+ boolean isRegistered(Class type);
+
+ /**
+ * Return any existing {@link InstanceFactory} for the given type.
+ * @param the instance type
+ * @param type the instance type
+ * @return the registered {@link InstanceSupplier} or {@code null}
+ */
+ InstanceSupplier getRegisteredInstanceSupplier(Class type);
+
+ /**
+ * Add an {@link ApplicationListener} that will be called with a
+ * {@link BootstrapContextClosedEvent} when the {@link BootstrapContext} is closed and
+ * the {@link ApplicationContext} has been prepared.
+ * @param listener the listener to add
+ */
+ void addCloseListener(ApplicationListener listener);
+
+ /**
+ * Supplier used to provide the actual instance the first time it is accessed.
+ *
+ * @param the instance type
+ */
+ public interface InstanceSupplier {
+
+ /**
+ * Factory method used to create the instance when needed.
+ * @param context the {@link BootstrapContext} which may be used to obtain other
+ * bootstrap instances.
+ * @return the instance
+ */
+ T get(BootstrapContext context);
+
+ /**
+ * Factory method that can be used to create a {@link InstanceFactory} for a given
+ * instance.
+ * @param the instance type
+ * @param instance the instance
+ * @return a new {@link InstanceFactory}
+ */
+ static InstanceSupplier of(T instance) {
+ return (registry) -> instance;
+ }
+
+ /**
+ * Factory method that can be used to create a {@link InstanceFactory} from a
+ * {@link Supplier}.
+ * @param the instance type
+ * @param supplier the supplier that will provide the instance
+ * @return a new {@link InstanceFactory}
+ */
+ static InstanceSupplier from(Supplier supplier) {
+ return (registry) -> (supplier != null) ? supplier.get() : null;
+ }
+
+ }
+
+}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/Bootstrapper.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/Bootstrapper.java
new file mode 100644
index 00000000000..4e67042bece
--- /dev/null
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/Bootstrapper.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2012-2020 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;
+
+/**
+ * Callback interface that can be used to initialize a {@link BootstrapRegistry} before it
+ * is used.
+ *
+ * @author Phillip Webb
+ * @since 2.4.0
+ * @see SpringApplication#addBootstrapper(Bootstrapper)
+ * @see BootstrapRegistry
+ */
+public interface Bootstrapper {
+
+ /**
+ * Initialize the given {@link BootstrapRegistry} with any required registrations.
+ * @param registry the registry to initialize
+ */
+ void intitialize(BootstrapRegistry registry);
+
+}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ConfigurableBootstrapContext.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ConfigurableBootstrapContext.java
new file mode 100644
index 00000000000..432c1a70784
--- /dev/null
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ConfigurableBootstrapContext.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2012-2020 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;
+
+/**
+ * A {@link BootstrapContext} that also provides configuration methods via the
+ * {@link BootstrapRegistry} interface.
+ *
+ * @author Phillip Webb
+ * @since 2.4.0
+ * @see BootstrapRegistry
+ * @see BootstrapContext
+ * @see DefaultBootstrapContext
+ */
+public interface ConfigurableBootstrapContext extends BootstrapRegistry, BootstrapContext {
+
+}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/DefaultBootstrapContext.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/DefaultBootstrapContext.java
new file mode 100644
index 00000000000..9806d34916a
--- /dev/null
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/DefaultBootstrapContext.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2012-2020 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;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.event.ApplicationEventMulticaster;
+import org.springframework.context.event.SimpleApplicationEventMulticaster;
+import org.springframework.util.Assert;
+
+/**
+ * Default {@link ConfigurableBootstrapContext} implementation.
+ *
+ * @author Phillip Webb
+ * @since 2.4.0
+ */
+public class DefaultBootstrapContext implements ConfigurableBootstrapContext {
+
+ private final Map, InstanceSupplier>> instanceSuppliers = new HashMap<>();
+
+ private final Map, Object> instances = new HashMap<>();
+
+ private final ApplicationEventMulticaster events = new SimpleApplicationEventMulticaster();
+
+ @Override
+ public void register(Class type, InstanceSupplier instanceSupplier) {
+ register(type, instanceSupplier, true);
+ }
+
+ @Override
+ public void registerIfAbsent(Class type, InstanceSupplier instanceSupplier) {
+ register(type, instanceSupplier, false);
+ }
+
+ private void register(Class type, InstanceSupplier instanceSupplier, boolean replaceExisting) {
+ Assert.notNull(type, "Type must not be null");
+ Assert.notNull(instanceSupplier, "InstanceSupplier must not be null");
+ synchronized (this.instanceSuppliers) {
+ boolean alreadyRegistered = this.instanceSuppliers.containsKey(type);
+ if (replaceExisting || !alreadyRegistered) {
+ Assert.state(!this.instances.containsKey(type), () -> type.getName() + " has already been created");
+ this.instanceSuppliers.put(type, instanceSupplier);
+ }
+ }
+ }
+
+ @Override
+ public boolean isRegistered(Class type) {
+ synchronized (this.instanceSuppliers) {
+ return this.instanceSuppliers.containsKey(type);
+ }
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public InstanceSupplier getRegisteredInstanceSupplier(Class type) {
+ synchronized (this.instanceSuppliers) {
+ return (InstanceSupplier) this.instanceSuppliers.get(type);
+ }
+ }
+
+ @Override
+ public void addCloseListener(ApplicationListener listener) {
+ this.events.addApplicationListener(listener);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public T get(Class type) throws IllegalStateException {
+ synchronized (this.instanceSuppliers) {
+ InstanceSupplier> instanceSupplier = this.instanceSuppliers.get(type);
+ Assert.state(instanceSupplier != null, () -> type.getName() + " has not been registered");
+ return (T) this.instances.computeIfAbsent(type, (key) -> instanceSupplier.get(this));
+ }
+ }
+
+ /**
+ * Method to be called when {@link BootstrapContext} is closed and the
+ * {@link ApplicationContext} is prepared.
+ * @param applicationContext the prepared context
+ */
+ public void close(ConfigurableApplicationContext applicationContext) {
+ this.events.multicastEvent(new BootstrapContextClosedEvent(this, applicationContext));
+ }
+
+}
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 f9462f36eac..13f22f0cc9b 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
@@ -236,6 +236,8 @@ public class SpringApplication {
private Map defaultProperties;
+ private List bootstrappers;
+
private Set additionalProfiles = Collections.emptySet();
private boolean allowBeanDefinitionOverriding;
@@ -278,6 +280,7 @@ public class SpringApplication {
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
+ this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
@@ -307,21 +310,22 @@ public class SpringApplication {
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
+ DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
Collection exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
- listeners.starting(this.mainApplicationClass);
+ listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
- ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
+ ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class>[] { ConfigurableApplicationContext.class }, context);
- prepareContext(context, environment, listeners, applicationArguments, printedBanner);
+ prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
@@ -346,13 +350,19 @@ public class SpringApplication {
return context;
}
+ private DefaultBootstrapContext createBootstrapContext() {
+ DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
+ this.bootstrappers.forEach((initializer) -> initializer.intitialize(bootstrapContext));
+ return bootstrapContext;
+ }
+
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
- ApplicationArguments applicationArguments) {
+ DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
- listeners.environmentPrepared(environment);
+ listeners.environmentPrepared(bootstrapContext, environment);
DefaultPropertiesPropertySource.moveToEnd(environment);
configureAdditionalProfiles(environment);
bindToSpringApplication(environment);
@@ -375,12 +385,14 @@ public class SpringApplication {
}
}
- private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
- SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
+ private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
+ ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
+ ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
+ bootstrapContext.close(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
@@ -1028,6 +1040,17 @@ public class SpringApplication {
this.addConversionService = addConversionService;
}
+ /**
+ * Adds a {@link Bootstrapper} that can be used to initialize the
+ * {@link BootstrapRegistry}.
+ * @param bootstrapper the bootstraper
+ * @since 2.4.0
+ */
+ public void addBootstrapper(Bootstrapper bootstrapper) {
+ Assert.notNull(bootstrapper, "Bootstrapper must not be null");
+ this.bootstrappers.add(bootstrapper);
+ }
+
/**
* Set default environment properties which will be used in addition to those in the
* existing {@link Environment}.
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplicationRunListener.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplicationRunListener.java
index 325febe884a..5b4fefb3b57 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplicationRunListener.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplicationRunListener.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2019 the original author or authors.
+ * Copyright 2012-2020 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.
@@ -38,15 +38,40 @@ public interface SpringApplicationRunListener {
/**
* Called immediately when the run method has first started. Can be used for very
* early initialization.
+ * @param bootstrapContext the bootstrap context
*/
+ default void starting(ConfigurableBootstrapContext bootstrapContext) {
+ starting();
+ }
+
+ /**
+ * Called immediately when the run method has first started. Can be used for very
+ * early initialization.
+ * @deprecated since 2.4.0 in favor of {@link #starting(ConfigurableBootstrapContext)}
+ */
+ @Deprecated
default void starting() {
}
/**
* Called once the environment has been prepared, but before the
* {@link ApplicationContext} has been created.
+ * @param bootstrapContext the bootstrap context
* @param environment the environment
*/
+ default void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
+ ConfigurableEnvironment environment) {
+ environmentPrepared(environment);
+ }
+
+ /**
+ * Called once the environment has been prepared, but before the
+ * {@link ApplicationContext} has been created.
+ * @param environment the environment
+ * @deprecated since 2.4.0 in favor of
+ * {@link #environmentPrepared(ConfigurableBootstrapContext, ConfigurableEnvironment)}
+ */
+ @Deprecated
default void environmentPrepared(ConfigurableEnvironment environment) {
}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplicationRunListeners.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplicationRunListeners.java
index 4acb1a92706..21c26ef8fa5 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplicationRunListeners.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplicationRunListeners.java
@@ -49,17 +49,18 @@ class SpringApplicationRunListeners {
this.applicationStartup = applicationStartup;
}
- void starting(Class> mainApplicationClass) {
- doWithListeners("spring.boot.application.starting", SpringApplicationRunListener::starting, (step) -> {
- if (mainApplicationClass != null) {
- step.tag("mainApplicationClass", mainApplicationClass.getName());
- }
- });
+ void starting(ConfigurableBootstrapContext bootstrapContext, Class> mainApplicationClass) {
+ doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),
+ (step) -> {
+ if (mainApplicationClass != null) {
+ step.tag("mainApplicationClass", mainApplicationClass.getName());
+ }
+ });
}
- void environmentPrepared(ConfigurableEnvironment environment) {
+ void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
doWithListeners("spring.boot.application.environment-prepared",
- (listener) -> listener.environmentPrepared(environment));
+ (listener) -> listener.environmentPrepared(bootstrapContext, environment));
}
void contextPrepared(ConfigurableApplicationContext context) {
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/builder/SpringApplicationBuilder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/builder/SpringApplicationBuilder.java
index 8b7045e13a2..289d8219ca6 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/builder/SpringApplicationBuilder.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/builder/SpringApplicationBuilder.java
@@ -30,6 +30,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.boot.ApplicationContextFactory;
import org.springframework.boot.Banner;
+import org.springframework.boot.BootstrapRegistry;
+import org.springframework.boot.Bootstrapper;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.convert.ApplicationConversionService;
@@ -397,6 +399,18 @@ public class SpringApplicationBuilder {
return this;
}
+ /**
+ * Adds a {@link Bootstrapper} that can be used to initialize the
+ * {@link BootstrapRegistry}.
+ * @param bootstrapper the bootstraper
+ * @return the current builder
+ * @since 2.4.0
+ */
+ public SpringApplicationBuilder addBootstrapper(Bootstrapper bootstrapper) {
+ this.application.addBootstrapper(bootstrapper);
+ return this;
+ }
+
/**
* Flag to control whether the application should be initialized lazily.
* @param lazyInitialization the flag to set. Defaults to false.
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java
index 2daf112d9f4..d3a0eba3547 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java
@@ -25,6 +25,7 @@ import java.util.Set;
import org.apache.commons.logging.Log;
+import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.DefaultPropertiesPropertySource;
import org.springframework.boot.context.config.ConfigDataEnvironmentContributors.BinderOption;
import org.springframework.boot.context.properties.bind.BindException;
@@ -33,7 +34,6 @@ import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.bind.PlaceholdersResolver;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
-import org.springframework.boot.env.BootstrapRegistry;
import org.springframework.boot.logging.DeferredLogFactory;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
@@ -97,7 +97,7 @@ class ConfigDataEnvironment {
private final Log logger;
- private final BootstrapRegistry bootstrapRegistry;
+ private final ConfigurableBootstrapContext bootstrapContext;
private final ConfigurableEnvironment environment;
@@ -112,12 +112,12 @@ class ConfigDataEnvironment {
/**
* Create a new {@link ConfigDataEnvironment} instance.
* @param logFactory the deferred log factory
- * @param bootstrapRegistry the bootstrap registry
+ * @param bootstrapContext the bootstrap context
* @param environment the Spring {@link Environment}.
* @param resourceLoader {@link ResourceLoader} to load resource locations
* @param additionalProfiles any additional profiles to activate
*/
- ConfigDataEnvironment(DeferredLogFactory logFactory, BootstrapRegistry bootstrapRegistry,
+ ConfigDataEnvironment(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext,
ConfigurableEnvironment environment, ResourceLoader resourceLoader, Collection additionalProfiles) {
Binder binder = Binder.get(environment);
UseLegacyConfigProcessingException.throwIfRequested(binder);
@@ -126,17 +126,20 @@ class ConfigDataEnvironment {
.orElse(ConfigDataLocationNotFoundAction.FAIL);
this.logFactory = logFactory;
this.logger = logFactory.getLog(getClass());
- this.bootstrapRegistry = bootstrapRegistry;
+ this.bootstrapContext = bootstrapContext;
this.environment = environment;
- this.resolvers = createConfigDataLocationResolvers(logFactory, locationNotFoundAction, binder, resourceLoader);
+ this.resolvers = createConfigDataLocationResolvers(logFactory, bootstrapContext, locationNotFoundAction, binder,
+ resourceLoader);
this.additionalProfiles = additionalProfiles;
- this.loaders = new ConfigDataLoaders(logFactory, locationNotFoundAction);
+ this.loaders = new ConfigDataLoaders(logFactory, bootstrapContext, locationNotFoundAction);
this.contributors = createContributors(binder);
}
protected ConfigDataLocationResolvers createConfigDataLocationResolvers(DeferredLogFactory logFactory,
- ConfigDataLocationNotFoundAction locationNotFoundAction, Binder binder, ResourceLoader resourceLoader) {
- return new ConfigDataLocationResolvers(logFactory, locationNotFoundAction, binder, resourceLoader);
+ ConfigurableBootstrapContext bootstrapContext, ConfigDataLocationNotFoundAction locationNotFoundAction,
+ Binder binder, ResourceLoader resourceLoader) {
+ return new ConfigDataLocationResolvers(logFactory, bootstrapContext, locationNotFoundAction, binder,
+ resourceLoader);
}
private ConfigDataEnvironmentContributors createContributors(Binder binder) {
@@ -159,7 +162,7 @@ class ConfigDataEnvironment {
this.logger.trace("Creating wrapped config data contributor for default property source");
contributors.add(ConfigDataEnvironmentContributor.ofExisting(defaultPropertySource));
}
- return new ConfigDataEnvironmentContributors(this.logFactory, this.bootstrapRegistry, contributors);
+ return new ConfigDataEnvironmentContributors(this.logFactory, this.bootstrapContext, contributors);
}
ConfigDataEnvironmentContributors getContributors() {
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributors.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributors.java
index 6526e5a8666..053c2d06612 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributors.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributors.java
@@ -28,6 +28,7 @@ import java.util.stream.Stream;
import org.apache.commons.logging.Log;
+import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.context.config.ConfigDataEnvironmentContributor.ImportPhase;
import org.springframework.boot.context.config.ConfigDataEnvironmentContributor.Kind;
import org.springframework.boot.context.properties.bind.BindContext;
@@ -37,7 +38,6 @@ import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.bind.PlaceholdersResolver;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
-import org.springframework.boot.env.BootstrapRegistry;
import org.springframework.boot.logging.DeferredLogFactory;
import org.springframework.boot.origin.Origin;
import org.springframework.core.log.LogMessage;
@@ -56,25 +56,25 @@ class ConfigDataEnvironmentContributors implements Iterable contributors) {
this.logger = logFactory.getLog(getClass());
- this.bootstrapRegistry = bootstrapRegistry;
+ this.bootstrapContext = bootstrapContext;
this.root = ConfigDataEnvironmentContributor.of(contributors);
}
- private ConfigDataEnvironmentContributors(Log logger, BootstrapRegistry bootstrapRegistry,
+ private ConfigDataEnvironmentContributors(Log logger, ConfigurableBootstrapContext bootstrapContext,
ConfigDataEnvironmentContributor root) {
this.logger = logger;
- this.bootstrapRegistry = bootstrapRegistry;
+ this.bootstrapContext = bootstrapContext;
this.root = root;
}
@@ -107,7 +107,7 @@ class ConfigDataEnvironmentContributors implements Iterable additionalProfiles) {
- return new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry, environment, resourceLoader,
+ return new ConfigDataEnvironment(this.logFactory, this.bootstrapContext, environment, resourceLoader,
additionalProfiles);
}
@@ -125,13 +126,13 @@ public class ConfigDataEnvironmentPostProcessor implements EnvironmentPostProces
* directly and not necessarily as part of a {@link SpringApplication}.
* @param environment the environment to apply {@link ConfigData} to
* @param resourceLoader the resource loader to use
- * @param bootstrapRegistry the bootstrap registry to use or {@code null} to use a
- * throw-away registry
+ * @param bootstrapContext the bootstrap context to use or {@code null} to use a
+ * throw-away context
* @param additionalProfiles any additional profiles that should be applied
*/
public static void applyTo(ConfigurableEnvironment environment, ResourceLoader resourceLoader,
- BootstrapRegistry bootstrapRegistry, String... additionalProfiles) {
- applyTo(environment, resourceLoader, bootstrapRegistry, Arrays.asList(additionalProfiles));
+ ConfigurableBootstrapContext bootstrapContext, String... additionalProfiles) {
+ applyTo(environment, resourceLoader, bootstrapContext, Arrays.asList(additionalProfiles));
}
/**
@@ -140,16 +141,16 @@ public class ConfigDataEnvironmentPostProcessor implements EnvironmentPostProces
* directly and not necessarily as part of a {@link SpringApplication}.
* @param environment the environment to apply {@link ConfigData} to
* @param resourceLoader the resource loader to use
- * @param bootstrapRegistry the bootstrap registry to use or {@code null} to use a
- * throw-away registry
+ * @param bootstrapContext the bootstrap context to use or {@code null} to use a
+ * throw-away context
* @param additionalProfiles any additional profiles that should be applied
*/
public static void applyTo(ConfigurableEnvironment environment, ResourceLoader resourceLoader,
- BootstrapRegistry bootstrapRegistry, Collection additionalProfiles) {
+ ConfigurableBootstrapContext bootstrapContext, Collection additionalProfiles) {
DeferredLogFactory logFactory = Supplier::get;
- bootstrapRegistry = (bootstrapRegistry != null) ? bootstrapRegistry : new DefaultBootstrapRegisty();
+ bootstrapContext = (bootstrapContext != null) ? bootstrapContext : new DefaultBootstrapContext();
ConfigDataEnvironmentPostProcessor postProcessor = new ConfigDataEnvironmentPostProcessor(logFactory,
- bootstrapRegistry);
+ bootstrapContext);
postProcessor.postProcessEnvironment(environment, resourceLoader, additionalProfiles);
}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoader.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoader.java
index 921fdb8057f..8c3fa0f6b17 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoader.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoader.java
@@ -20,6 +20,10 @@ import java.io.IOException;
import org.apache.commons.logging.Log;
+import org.springframework.boot.BootstrapContext;
+import org.springframework.boot.BootstrapRegistry;
+import org.springframework.boot.ConfigurableBootstrapContext;
+
/**
* Strategy class that can be used used to load {@link ConfigData} instances from a
* {@link ConfigDataLocation location}. Implementations should be added as a
@@ -27,6 +31,9 @@ import org.apache.commons.logging.Log;
* supported:
*
* - {@link Log} - if the resolver needs deferred logging
+ * - {@link ConfigurableBootstrapContext} - A bootstrap context that can be used to
+ * store objects that may be expensive to create, or need to be shared
+ * ({@link BootstrapContext} or {@link BootstrapRegistry} may also be used).
*
*
* Multiple loaders cannot claim the same location.
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoaderContext.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoaderContext.java
index e46211a6f32..50e0bf45de8 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoaderContext.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoaderContext.java
@@ -16,7 +16,7 @@
package org.springframework.boot.context.config;
-import org.springframework.boot.env.BootstrapRegistry;
+import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.env.EnvironmentPostProcessor;
/**
@@ -28,10 +28,10 @@ import org.springframework.boot.env.EnvironmentPostProcessor;
public interface ConfigDataLoaderContext {
/**
- * Provides access to the {@link BootstrapRegistry} shared across all
+ * Provides access to the {@link ConfigurableBootstrapContext} shared across all
* {@link EnvironmentPostProcessor EnvironmentPostProcessors}.
- * @return the bootstrap registry
+ * @return the bootstrap context
*/
- BootstrapRegistry getBootstrapRegistry();
+ ConfigurableBootstrapContext getBootstrapContext();
}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoaders.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoaders.java
index 3688649da5b..e7cc4e1fc52 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoaders.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoaders.java
@@ -23,6 +23,9 @@ import java.util.List;
import org.apache.commons.logging.Log;
+import org.springframework.boot.BootstrapContext;
+import org.springframework.boot.BootstrapRegistry;
+import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.logging.DeferredLogFactory;
import org.springframework.boot.util.Instantiator;
import org.springframework.core.ResolvableType;
@@ -48,27 +51,36 @@ class ConfigDataLoaders {
/**
* Create a new {@link ConfigDataLoaders} instance.
+ * @param logFactory the deferred log factory
+ * @param bootstrapContext the bootstrap context
* @param locationNotFoundAction the action to take if a
* {@link ConfigDataLocationNotFoundException} is thrown
- * @param logFactory the deferred log factory
*/
- ConfigDataLoaders(DeferredLogFactory logFactory, ConfigDataLocationNotFoundAction locationNotFoundAction) {
- this(logFactory, locationNotFoundAction, SpringFactoriesLoader.loadFactoryNames(ConfigDataLoader.class, null));
+ ConfigDataLoaders(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext,
+ ConfigDataLocationNotFoundAction locationNotFoundAction) {
+ this(logFactory, bootstrapContext, locationNotFoundAction,
+ SpringFactoriesLoader.loadFactoryNames(ConfigDataLoader.class, null));
}
/**
* Create a new {@link ConfigDataLoaders} instance.
* @param logFactory the deferred log factory
+ * @param bootstrapContext the bootstrap context
* @param locationNotFoundAction the action to take if a
* {@link ConfigDataLocationNotFoundException} is thrown
* @param names the {@link ConfigDataLoader} class names instantiate
*/
- ConfigDataLoaders(DeferredLogFactory logFactory, ConfigDataLocationNotFoundAction locationNotFoundAction,
- List names) {
+ ConfigDataLoaders(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext,
+ ConfigDataLocationNotFoundAction locationNotFoundAction, List names) {
this.logger = logFactory.getLog(getClass());
this.locationNotFoundAction = locationNotFoundAction;
Instantiator> instantiator = new Instantiator<>(ConfigDataLoader.class,
- (availableParameters) -> availableParameters.add(Log.class, logFactory::getLog));
+ (availableParameters) -> {
+ availableParameters.add(Log.class, logFactory::getLog);
+ availableParameters.add(ConfigurableBootstrapContext.class, bootstrapContext);
+ availableParameters.add(BootstrapContext.class, bootstrapContext);
+ availableParameters.add(BootstrapRegistry.class, bootstrapContext);
+ });
this.loaders = instantiator.instantiate(names);
this.locationTypes = getLocationTypes(this.loaders);
}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolver.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolver.java
index ab18ac3daee..a29e88196a6 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolver.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolver.java
@@ -21,6 +21,9 @@ import java.util.List;
import org.apache.commons.logging.Log;
+import org.springframework.boot.BootstrapContext;
+import org.springframework.boot.BootstrapRegistry;
+import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
@@ -36,6 +39,9 @@ import org.springframework.core.io.ResourceLoader;
* {@link Binder} - if the resolver needs to obtain values from the initial
* {@link Environment}
* {@link ResourceLoader} - if the resolver needs a resource loader
+ * {@link ConfigurableBootstrapContext} - A bootstrap context that can be used to
+ * store objects that may be expensive to create, or need to be shared
+ * ({@link BootstrapContext} or {@link BootstrapRegistry} may also be used).
*
*
* Resolvers may implement {@link Ordered} or use the {@link Order @Order} annotation. The
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolverContext.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolverContext.java
index eadd8a26581..fa8a8bedc4d 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolverContext.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolverContext.java
@@ -16,8 +16,8 @@
package org.springframework.boot.context.config;
+import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.context.properties.bind.Binder;
-import org.springframework.boot.env.BootstrapRegistry;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.boot.origin.Origin;
@@ -45,11 +45,11 @@ public interface ConfigDataLocationResolverContext {
ConfigDataLocation getParent();
/**
- * Provides access to the {@link BootstrapRegistry} shared across all
+ * Provides access to the {@link ConfigurableBootstrapContext} shared across all
* {@link EnvironmentPostProcessor EnvironmentPostProcessors}.
- * @return the bootstrap registry
+ * @return the bootstrap context
*/
- BootstrapRegistry getBootstrapRegistry();
+ ConfigurableBootstrapContext getBootstrapContext();
/**
* Return the {@link Origin} of a location that's being resolved.
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolvers.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolvers.java
index 422d936a142..6b07ffb14e8 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolvers.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolvers.java
@@ -23,6 +23,9 @@ import java.util.function.Supplier;
import org.apache.commons.logging.Log;
+import org.springframework.boot.BootstrapContext;
+import org.springframework.boot.BootstrapRegistry;
+import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.logging.DeferredLogFactory;
import org.springframework.boot.util.Instantiator;
@@ -50,28 +53,31 @@ class ConfigDataLocationResolvers {
/**
* Create a new {@link ConfigDataLocationResolvers} instance.
* @param logFactory a {@link DeferredLogFactory} used to inject {@link Log} instances
+ * @param bootstrapContext the bootstrap context
* @param locationNotFoundAction the action to take if a
* {@link ConfigDataLocationNotFoundException} is thrown
* @param binder a binder providing values from the initial {@link Environment}
* @param resourceLoader {@link ResourceLoader} to load resource locations
*/
- ConfigDataLocationResolvers(DeferredLogFactory logFactory, ConfigDataLocationNotFoundAction locationNotFoundAction,
- Binder binder, ResourceLoader resourceLoader) {
- this(logFactory, locationNotFoundAction, binder, resourceLoader,
+ ConfigDataLocationResolvers(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext,
+ ConfigDataLocationNotFoundAction locationNotFoundAction, Binder binder, ResourceLoader resourceLoader) {
+ this(logFactory, bootstrapContext, locationNotFoundAction, binder, resourceLoader,
SpringFactoriesLoader.loadFactoryNames(ConfigDataLocationResolver.class, null));
}
/**
* Create a new {@link ConfigDataLocationResolvers} instance.
* @param logFactory a {@link DeferredLogFactory} used to inject {@link Log} instances
+ * @param bootstrapContext the bootstrap context
* @param locationNotFoundAction the action to take if a
* {@link ConfigDataLocationNotFoundException} is thrown
* @param binder {@link Binder} providing values from the initial {@link Environment}
* @param resourceLoader {@link ResourceLoader} to load resource locations
* @param names the {@link ConfigDataLocationResolver} class names
*/
- ConfigDataLocationResolvers(DeferredLogFactory logFactory, ConfigDataLocationNotFoundAction locationNotFoundAction,
- Binder binder, ResourceLoader resourceLoader, List names) {
+ ConfigDataLocationResolvers(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext,
+ ConfigDataLocationNotFoundAction locationNotFoundAction, Binder binder, ResourceLoader resourceLoader,
+ List names) {
this.logger = logFactory.getLog(getClass());
this.locationNotFoundAction = locationNotFoundAction;
Instantiator> instantiator = new Instantiator<>(ConfigDataLocationResolver.class,
@@ -79,6 +85,9 @@ class ConfigDataLocationResolvers {
availableParameters.add(Log.class, logFactory::getLog);
availableParameters.add(Binder.class, binder);
availableParameters.add(ResourceLoader.class, resourceLoader);
+ availableParameters.add(ConfigurableBootstrapContext.class, bootstrapContext);
+ availableParameters.add(BootstrapContext.class, bootstrapContext);
+ availableParameters.add(BootstrapRegistry.class, bootstrapContext);
});
this.resolvers = reorder(instantiator.instantiate(names));
}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/event/ApplicationEnvironmentPreparedEvent.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/event/ApplicationEnvironmentPreparedEvent.java
index 167dfa64240..00d24f8b1a7 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/event/ApplicationEnvironmentPreparedEvent.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/event/ApplicationEnvironmentPreparedEvent.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2019 the original author or authors.
+ * Copyright 2012-2020 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.
@@ -16,6 +16,7 @@
package org.springframework.boot.context.event;
+import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.SpringApplication;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
@@ -30,6 +31,8 @@ import org.springframework.core.env.Environment;
@SuppressWarnings("serial")
public class ApplicationEnvironmentPreparedEvent extends SpringApplicationEvent {
+ private final ConfigurableBootstrapContext bootstrapContext;
+
private final ConfigurableEnvironment environment;
/**
@@ -37,13 +40,38 @@ public class ApplicationEnvironmentPreparedEvent extends SpringApplicationEvent
* @param application the current application
* @param args the arguments the application is running with
* @param environment the environment that was just created
+ * @deprecated since 2.4.0 in favor of
+ * {@link #ApplicationEnvironmentPreparedEvent(ConfigurableBootstrapContext, SpringApplication, String[], ConfigurableEnvironment)}
*/
+ @Deprecated
public ApplicationEnvironmentPreparedEvent(SpringApplication application, String[] args,
ConfigurableEnvironment environment) {
+ this(null, application, args, environment);
+ }
+
+ /**
+ * Create a new {@link ApplicationEnvironmentPreparedEvent} instance.
+ * @param bootstrapContext the bootstrap context
+ * @param application the current application
+ * @param args the arguments the application is running with
+ * @param environment the environment that was just created
+ */
+ public ApplicationEnvironmentPreparedEvent(ConfigurableBootstrapContext bootstrapContext,
+ SpringApplication application, String[] args, ConfigurableEnvironment environment) {
super(application, args);
+ this.bootstrapContext = bootstrapContext;
this.environment = environment;
}
+ /**
+ * Return the bootstap context.
+ * @return the bootstrap context
+ * @since 2.4.0
+ */
+ public ConfigurableBootstrapContext getBootstrapContext() {
+ return this.bootstrapContext;
+ }
+
/**
* Return the environment.
* @return the environment
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/event/ApplicationStartingEvent.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/event/ApplicationStartingEvent.java
index e6587420099..b3b66132d35 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/event/ApplicationStartingEvent.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/event/ApplicationStartingEvent.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2019 the original author or authors.
+ * Copyright 2012-2020 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.
@@ -16,6 +16,7 @@
package org.springframework.boot.context.event;
+import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
@@ -35,13 +36,39 @@ import org.springframework.core.env.Environment;
@SuppressWarnings("serial")
public class ApplicationStartingEvent extends SpringApplicationEvent {
+ private final ConfigurableBootstrapContext bootstrapContext;
+
/**
* Create a new {@link ApplicationStartingEvent} instance.
* @param application the current application
* @param args the arguments the application is running with
+ * @deprecated since 2.4.0 in favor of
+ * {@link #ApplicationStartingEvent(ConfigurableBootstrapContext, SpringApplication, String[])}
*/
+ @Deprecated
public ApplicationStartingEvent(SpringApplication application, String[] args) {
+ this(null, application, args);
+ }
+
+ /**
+ * Create a new {@link ApplicationStartingEvent} instance.
+ * @param bootstrapContext the bootstrap context
+ * @param application the current application
+ * @param args the arguments the application is running with
+ */
+ public ApplicationStartingEvent(ConfigurableBootstrapContext bootstrapContext, SpringApplication application,
+ String[] args) {
super(application, args);
+ this.bootstrapContext = bootstrapContext;
+ }
+
+ /**
+ * Return the bootstap context.
+ * @return the bootstrap context
+ * @since 2.4.0
+ */
+ public ConfigurableBootstrapContext getBootstrapContext() {
+ return this.bootstrapContext;
}
}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/event/EventPublishingRunListener.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/event/EventPublishingRunListener.java
index bd095161a19..fcc94af3850 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/event/EventPublishingRunListener.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/event/EventPublishingRunListener.java
@@ -19,6 +19,7 @@ package org.springframework.boot.context.event;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.boot.availability.AvailabilityChangeEvent;
@@ -70,14 +71,16 @@ public class EventPublishingRunListener implements SpringApplicationRunListener,
}
@Override
- public void starting() {
- this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
+ public void starting(ConfigurableBootstrapContext bootstrapContext) {
+ this.initialMulticaster
+ .multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));
}
@Override
- public void environmentPrepared(ConfigurableEnvironment environment) {
- this.initialMulticaster
- .multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
+ public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
+ ConfigurableEnvironment environment) {
+ this.initialMulticaster.multicastEvent(
+ new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment));
}
@Override
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/BootstrapRegistry.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/BootstrapRegistry.java
deleted file mode 100644
index 030c1ee56de..00000000000
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/BootstrapRegistry.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright 2012-2020 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.env;
-
-import java.util.function.BiConsumer;
-import java.util.function.Supplier;
-
-import org.springframework.boot.context.event.ApplicationPreparedEvent;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.ConfigurableApplicationContext;
-import org.springframework.core.env.Environment;
-
-/**
- * A simple object registry that is available during {@link Environment} post-processing
- * up to the point that the {@link ApplicationContext} is prepared. The registry can be
- * used to store objects that may be expensive to create, or need to be shared by
- * different {@link EnvironmentPostProcessor EnvironmentPostProcessors}.
- *
- * The registry uses the object type as a key, meaning that only a single instance of a
- * given class can be stored.
- *
- * Registered instances may optionally use
- * {@link Registration#onApplicationContextPrepared(BiConsumer)
- * onApplicationContextPrepared(...)} to perform an action when the
- * {@link ApplicationContext} is {@link ApplicationPreparedEvent prepared}. For example,
- * an instance may choose to register itself as a regular Spring bean so that it is
- * available for the application to use.
- *
- * @author Phillip Webb
- * @since 2.4.0
- * @see EnvironmentPostProcessor
- */
-public interface BootstrapRegistry {
-
- /**
- * Get an instance from the registry, creating one if it does not already exist.
- * @param the instance type
- * @param type the instance type
- * @param instanceSupplier a supplier used to create the instance if it doesn't
- * already exist
- * @return the registered instance
- */
- T get(Class type, Supplier instanceSupplier);
-
- /**
- * Get an instance from the registry, creating one if it does not already exist.
- * @param the instance type
- * @param type the instance type
- * @param instanceSupplier a supplier used to create the instance if it doesn't
- * already exist
- * @param onApplicationContextPreparedAction the action that should be called when the
- * application context is prepared. This action is ignored if the registration already
- * exists.
- * @return the registered instance
- */
- T get(Class type, Supplier instanceSupplier,
- BiConsumer onApplicationContextPreparedAction);
-
- /**
- * Register an instance with the registry and return a {@link Registration} that can
- * be used to provide further configuration. This method will replace any existing
- * registration.
- * @param the instance type
- * @param type the instance type
- * @param instanceSupplier a supplier used to create the instance if it doesn't
- * already exist
- * @return an instance registration
- */
- Registration register(Class type, Supplier instanceSupplier);
-
- /**
- * Return if a registration exists for the given type.
- * @param the instance type
- * @param type the instance type
- * @return {@code true} if the type has already been registered
- */
- boolean isRegistered(Class type);
-
- /**
- * Return any existing {@link Registration} for the given type.
- * @param the instance type
- * @param type the instance type
- * @return the existing registration or {@code null}
- */
- Registration getRegistration(Class type);
-
- /**
- * A single registration contained in the registry.
- *
- * @param the instance type
- */
- interface Registration {
-
- /**
- * Get or crearte the registered object instance.
- * @return the object instance
- */
- T get();
-
- /**
- * Add an action that should run when the {@link ApplicationContext} has been
- * prepared.
- * @param action the action to run
- */
- void onApplicationContextPrepared(BiConsumer action);
-
- }
-
-}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/DefaultBootstrapRegisty.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/DefaultBootstrapRegisty.java
deleted file mode 100644
index b1c214cdf57..00000000000
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/DefaultBootstrapRegisty.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright 2012-2020 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.env;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.function.BiConsumer;
-import java.util.function.Supplier;
-
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.ConfigurableApplicationContext;
-
-/**
- * Default implementation of {@link BootstrapRegistry}.
- *
- * @author Phillip Webb
- * @since 2.4.0
- */
-public class DefaultBootstrapRegisty implements BootstrapRegistry {
-
- private final Map, DefaultRegistration>> registrations = new HashMap<>();
-
- @Override
- public T get(Class type, Supplier instanceSupplier) {
- return get(type, instanceSupplier, null);
- }
-
- @Override
- public T get(Class type, Supplier instanceSupplier,
- BiConsumer onApplicationContextPreparedAction) {
- Registration registration = getRegistration(type);
- if (registration != null) {
- return registration.get();
- }
- registration = register(type, instanceSupplier);
- registration.onApplicationContextPrepared(onApplicationContextPreparedAction);
- return registration.get();
- }
-
- @Override
- public Registration register(Class type, Supplier instanceSupplier) {
- DefaultRegistration registration = new DefaultRegistration<>(instanceSupplier);
- this.registrations.put(type, registration);
- return registration;
- }
-
- @Override
- public boolean isRegistered(Class type) {
- return getRegistration(type) != null;
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public Registration getRegistration(Class type) {
- return (Registration) this.registrations.get(type);
- }
-
- /**
- * Method to be called when the {@link ApplicationContext} is prepared.
- * @param applicationContext the prepared context
- */
- public void applicationContextPrepared(ConfigurableApplicationContext applicationContext) {
- this.registrations.values()
- .forEach((registration) -> registration.applicationContextPrepared(applicationContext));
- }
-
- /**
- * Clear the registry to reclaim memory.
- */
- public void clear() {
- this.registrations.clear();
- }
-
- /**
- * Default implementation of {@link Registration}.
- */
- private static class DefaultRegistration implements Registration {
-
- private Supplier instanceSupplier;
-
- private volatile T instance;
-
- private List> applicationContextPreparedActions = new ArrayList<>();
-
- DefaultRegistration(Supplier instanceSupplier) {
- this.instanceSupplier = instanceSupplier;
- }
-
- @Override
- public T get() {
- T instance = this.instance;
- if (instance == null) {
- synchronized (this.instanceSupplier) {
- instance = this.instanceSupplier.get();
- this.instance = instance;
- }
- }
- return instance;
- }
-
- @Override
- public void onApplicationContextPrepared(BiConsumer action) {
- if (action != null) {
- this.applicationContextPreparedActions.add(action);
- }
- }
-
- /**
- * Method called when the {@link ApplicationContext} is prepared.
- * @param applicationContext the prepared context
- */
- void applicationContextPrepared(ConfigurableApplicationContext applicationContext) {
- this.applicationContextPreparedActions.forEach((consumer) -> consumer.accept(applicationContext, get()));
- }
-
- }
-
-}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/EnvironmentPostProcessor.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/EnvironmentPostProcessor.java
index a9f6b47af5f..1ef5cf4e91d 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/EnvironmentPostProcessor.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/EnvironmentPostProcessor.java
@@ -18,6 +18,9 @@ package org.springframework.boot.env;
import org.apache.commons.logging.Log;
+import org.springframework.boot.BootstrapContext;
+import org.springframework.boot.BootstrapRegistry;
+import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.logging.DeferredLogFactory;
import org.springframework.core.env.ConfigurableEnvironment;
@@ -41,8 +44,9 @@ import org.springframework.core.env.Environment;
* itself to configure logging levels).
* {@link Log} - A log with output deferred until the application has been full
* prepared (allowing the environment itself to configure logging levels).
- * {@link BootstrapRegistry} - A bootstrap registry that can be used to store objects
- * that may be expensive to create, or need to be shared.
+ * {@link ConfigurableBootstrapContext} - A bootstrap context that can be used to
+ * store objects that may be expensive to create, or need to be shared
+ * ({@link BootstrapContext} or {@link BootstrapRegistry} may also be used).
*
*
* @author Andy Wilkinson
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/EnvironmentPostProcessorApplicationListener.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/EnvironmentPostProcessorApplicationListener.java
index 2e17928033f..e255031cca1 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/EnvironmentPostProcessorApplicationListener.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/EnvironmentPostProcessorApplicationListener.java
@@ -18,6 +18,7 @@ package org.springframework.boot.env;
import java.util.List;
+import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.context.event.ApplicationFailedEvent;
@@ -44,8 +45,6 @@ public class EnvironmentPostProcessorApplicationListener implements SmartApplica
private final DeferredLogs deferredLogs;
- private final DefaultBootstrapRegisty bootstrapRegistry;
-
private int order = DEFAULT_ORDER;
private final EnvironmentPostProcessorsFactory postProcessorsFactory;
@@ -65,14 +64,13 @@ public class EnvironmentPostProcessorApplicationListener implements SmartApplica
* @param postProcessorsFactory the post processors factory
*/
public EnvironmentPostProcessorApplicationListener(EnvironmentPostProcessorsFactory postProcessorsFactory) {
- this(postProcessorsFactory, new DeferredLogs(), new DefaultBootstrapRegisty());
+ this(postProcessorsFactory, new DeferredLogs());
}
EnvironmentPostProcessorApplicationListener(EnvironmentPostProcessorsFactory postProcessorsFactory,
- DeferredLogs deferredLogs, DefaultBootstrapRegisty bootstrapRegistry) {
+ DeferredLogs deferredLogs) {
this.postProcessorsFactory = postProcessorsFactory;
this.deferredLogs = deferredLogs;
- this.bootstrapRegistry = bootstrapRegistry;
}
@Override
@@ -98,13 +96,12 @@ public class EnvironmentPostProcessorApplicationListener implements SmartApplica
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment environment = event.getEnvironment();
SpringApplication application = event.getSpringApplication();
- for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors()) {
+ for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(event.getBootstrapContext())) {
postProcessor.postProcessEnvironment(environment, application);
}
}
private void onApplicationPreparedEvent(ApplicationPreparedEvent event) {
- this.bootstrapRegistry.applicationContextPrepared(event.getApplicationContext());
finish();
}
@@ -113,12 +110,11 @@ public class EnvironmentPostProcessorApplicationListener implements SmartApplica
}
private void finish() {
- this.bootstrapRegistry.clear();
this.deferredLogs.switchOverAll();
}
- List getEnvironmentPostProcessors() {
- return this.postProcessorsFactory.getEnvironmentPostProcessors(this.deferredLogs, this.bootstrapRegistry);
+ List getEnvironmentPostProcessors(ConfigurableBootstrapContext bootstrapContext) {
+ return this.postProcessorsFactory.getEnvironmentPostProcessors(this.deferredLogs, bootstrapContext);
}
@Override
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/EnvironmentPostProcessorsFactory.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/EnvironmentPostProcessorsFactory.java
index 1074ccace03..dbd3730c9fc 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/EnvironmentPostProcessorsFactory.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/EnvironmentPostProcessorsFactory.java
@@ -18,6 +18,7 @@ package org.springframework.boot.env;
import java.util.List;
+import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.logging.DeferredLogFactory;
import org.springframework.core.io.support.SpringFactoriesLoader;
@@ -34,11 +35,11 @@ public interface EnvironmentPostProcessorsFactory {
/**
* Create all requested {@link EnvironmentPostProcessor} instances.
* @param logFactory a deferred log factory
- * @param bootstrapRegistry a bootstrap registry
+ * @param bootstrapContext a bootstrap context
* @return the post processor instances
*/
List getEnvironmentPostProcessors(DeferredLogFactory logFactory,
- BootstrapRegistry bootstrapRegistry);
+ ConfigurableBootstrapContext bootstrapContext);
/**
* Return a {@link EnvironmentPostProcessorsFactory} backed by
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/ReflectionEnvironmentPostProcessorsFactory.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/ReflectionEnvironmentPostProcessorsFactory.java
index baab7439ddb..7b984ff3aef 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/ReflectionEnvironmentPostProcessorsFactory.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/ReflectionEnvironmentPostProcessorsFactory.java
@@ -21,6 +21,9 @@ import java.util.List;
import org.apache.commons.logging.Log;
+import org.springframework.boot.BootstrapContext;
+import org.springframework.boot.BootstrapRegistry;
+import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.logging.DeferredLogFactory;
import org.springframework.boot.util.Instantiator;
@@ -48,12 +51,14 @@ class ReflectionEnvironmentPostProcessorsFactory implements EnvironmentPostProce
@Override
public List getEnvironmentPostProcessors(DeferredLogFactory logFactory,
- BootstrapRegistry bootstrapRegistry) {
+ ConfigurableBootstrapContext bootstrapContext) {
Instantiator instantiator = new Instantiator<>(EnvironmentPostProcessor.class,
(parameters) -> {
parameters.add(DeferredLogFactory.class, logFactory);
parameters.add(Log.class, logFactory::getLog);
- parameters.add(BootstrapRegistry.class, bootstrapRegistry);
+ parameters.add(ConfigurableBootstrapContext.class, bootstrapContext);
+ parameters.add(BootstrapContext.class, bootstrapContext);
+ parameters.add(BootstrapRegistry.class, bootstrapContext);
});
return instantiator.instantiate(this.classNames);
}
diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/DefaultBootstrapContextTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/DefaultBootstrapContextTests.java
new file mode 100644
index 00000000000..e6003dc704f
--- /dev/null
+++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/DefaultBootstrapContextTests.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2012-2020 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;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.assertj.core.api.AbstractAssert;
+import org.assertj.core.api.AssertProvider;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.boot.BootstrapRegistry.InstanceSupplier;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.support.StaticApplicationContext;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
+
+/**
+ * Tests for {@link DefaultBootstrapContext}.
+ *
+ * @author Phillip Webb
+ */
+class DefaultBootstrapContextTests {
+
+ private DefaultBootstrapContext context = new DefaultBootstrapContext();
+
+ private AtomicInteger counter = new AtomicInteger();
+
+ private StaticApplicationContext applicationContext = new StaticApplicationContext();
+
+ @Test
+ void registerWhenTypeIsNullThrowsException() {
+ assertThatIllegalArgumentException().isThrownBy(() -> this.context.register(null, InstanceSupplier.of(1)))
+ .withMessage("Type must not be null");
+ }
+
+ @Test
+ void registerWhenRegistrationIsNullThrowException() {
+ assertThatIllegalArgumentException().isThrownBy(() -> this.context.register(Integer.class, null))
+ .withMessage("InstanceSupplier must not be null");
+ }
+
+ @Test
+ void registerWhenNotAlreadyRegisteredRegistersInstance() {
+ this.context.register(Integer.class, InstanceSupplier.from(this.counter::getAndIncrement));
+ assertThat(this.context.get(Integer.class)).isEqualTo(0);
+ assertThat(this.context.get(Integer.class)).isEqualTo(0);
+ }
+
+ @Test
+ void registerWhenAlreadyRegisteredRegistersReplacedInstance() {
+ this.context.register(Integer.class, InstanceSupplier.from(this.counter::getAndIncrement));
+ this.context.register(Integer.class, InstanceSupplier.of(100));
+ assertThat(this.context.get(Integer.class)).isEqualTo(100);
+ assertThat(this.context.get(Integer.class)).isEqualTo(100);
+ }
+
+ @Test
+ void registerWhenAlreadyCreatedThrowsException() {
+ this.context.register(Integer.class, InstanceSupplier.from(this.counter::getAndIncrement));
+ this.context.get(Integer.class);
+ assertThatIllegalStateException()
+ .isThrownBy(() -> this.context.register(Integer.class, InstanceSupplier.of(100)))
+ .withMessage("java.lang.Integer has already been created");
+ }
+
+ @Test
+ void registerWithDependencyRegistersInstance() {
+ this.context.register(Integer.class, InstanceSupplier.of(100));
+ this.context.register(String.class, this::integerAsString);
+ assertThat(this.context.get(String.class)).isEqualTo("100");
+ }
+
+ private String integerAsString(BootstrapContext context) {
+ return String.valueOf(context.get(Integer.class));
+ }
+
+ @Test
+ void registerIfAbsentWhenAbsentRegisters() {
+ this.context.registerIfAbsent(Long.class, InstanceSupplier.of(100L));
+ assertThat(this.context.get(Long.class)).isEqualTo(100L);
+ }
+
+ @Test
+ void registerIfAbsentWhenPresentDoesNotRegister() {
+ this.context.registerIfAbsent(Long.class, InstanceSupplier.of(1L));
+ this.context.registerIfAbsent(Long.class, InstanceSupplier.of(100L));
+ assertThat(this.context.get(Long.class)).isEqualTo(1L);
+ }
+
+ @Test
+ void isRegisteredWhenNotRegisteredReturnsFalse() {
+ this.context.register(Number.class, InstanceSupplier.of(1));
+ assertThat(this.context.isRegistered(Long.class)).isFalse();
+ }
+
+ @Test
+ void isRegisteredWhenRegisteredReturnsTrue() {
+ this.context.register(Number.class, InstanceSupplier.of(1));
+ assertThat(this.context.isRegistered(Number.class)).isTrue();
+ }
+
+ @Test
+ void getRegisteredInstanceSupplierWhenNotRegisteredReturnsNull() {
+ this.context.register(Number.class, InstanceSupplier.of(1));
+ assertThat(this.context.getRegisteredInstanceSupplier(Long.class)).isNull();
+ }
+
+ @Test
+ void getRegisteredInstanceSupplierWhenRegisteredReturnsRegistration() {
+ InstanceSupplier instanceSupplier = InstanceSupplier.of(1);
+ this.context.register(Number.class, instanceSupplier);
+ assertThat(this.context.getRegisteredInstanceSupplier(Number.class)).isSameAs(instanceSupplier);
+ }
+
+ @Test
+ void getWhenNoRegistrationThrowsIllegalStateException() {
+ this.context.register(Number.class, InstanceSupplier.of(1));
+ assertThatIllegalStateException().isThrownBy(() -> this.context.get(Long.class))
+ .withMessageContaining("has not been registered");
+ }
+
+ @Test
+ void getWhenRegisteredAsNullReturnsNull() {
+ this.context.register(Number.class, InstanceSupplier.of(null));
+ assertThat(this.context.get(Number.class)).isNull();
+ }
+
+ @Test
+ void getCreatesOnlyOneInstance() {
+ this.context.register(Integer.class, InstanceSupplier.from(this.counter::getAndIncrement));
+ assertThat(this.context.get(Integer.class)).isEqualTo(0);
+ assertThat(this.context.get(Integer.class)).isEqualTo(0);
+ }
+
+ @Test
+ void closeMulticastsEventToListeners() {
+ TestCloseListener listener = new TestCloseListener();
+ this.context.addCloseListener(listener);
+ assertThat(listener).wasNotCalled();
+ this.context.close(this.applicationContext);
+ assertThat(listener).wasCalledOnlyOnce().hasBootstrapContextSameAs(this.context)
+ .hasApplicationContextSameAs(this.applicationContext);
+ }
+
+ @Test
+ void addCloseListenerIgnoresMultipleCallsWithSameListener() {
+ TestCloseListener listener = new TestCloseListener();
+ this.context.addCloseListener(listener);
+ this.context.addCloseListener(listener);
+ this.context.close(this.applicationContext);
+ assertThat(listener).wasCalledOnlyOnce();
+ }
+
+ private static class TestCloseListener
+ implements ApplicationListener, AssertProvider {
+
+ private int called;
+
+ private BootstrapContext bootstrapContext;
+
+ private ConfigurableApplicationContext applicationContext;
+
+ @Override
+ public void onApplicationEvent(BootstrapContextClosedEvent event) {
+ this.called++;
+ this.bootstrapContext = event.getBootstrapContext();
+ this.applicationContext = event.getApplicationContext();
+ }
+
+ @Override
+ public CloseListenerAssert assertThat() {
+ return new CloseListenerAssert(this);
+ }
+
+ }
+
+ private static class CloseListenerAssert extends AbstractAssert {
+
+ CloseListenerAssert(TestCloseListener actual) {
+ super(actual, CloseListenerAssert.class);
+ }
+
+ CloseListenerAssert wasCalledOnlyOnce() {
+ assertThat(this.actual.called).as("action calls").isEqualTo(1);
+ return this;
+ }
+
+ CloseListenerAssert wasNotCalled() {
+ assertThat(this.actual.called).as("action calls").isEqualTo(0);
+ return this;
+ }
+
+ CloseListenerAssert hasBootstrapContextSameAs(BootstrapContext bootstrapContext) {
+ assertThat(this.actual.bootstrapContext).isSameAs(bootstrapContext);
+ return this;
+ }
+
+ CloseListenerAssert hasApplicationContextSameAs(ApplicationContext applicationContext) {
+ assertThat(this.actual.applicationContext).isSameAs(applicationContext);
+ return this;
+ }
+
+ }
+
+}
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 5541743f0e3..1610eb5ddbc 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
@@ -48,6 +48,7 @@ import org.springframework.beans.factory.support.BeanDefinitionOverrideException
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.DefaultBeanNameGenerator;
+import org.springframework.boot.BootstrapRegistry.InstanceSupplier;
import org.springframework.boot.availability.AvailabilityChangeEvent;
import org.springframework.boot.availability.AvailabilityState;
import org.springframework.boot.availability.LivenessState;
@@ -105,6 +106,8 @@ import org.springframework.core.metrics.ApplicationStartup;
import org.springframework.core.metrics.StartupStep;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.test.context.support.TestPropertySourceUtils;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.context.ConfigurableWebEnvironment;
import org.springframework.web.context.WebApplicationContext;
@@ -1201,6 +1204,35 @@ class SpringApplicationTests {
assertThat(startCount).isEqualTo(endCount);
}
+ @Test
+ void addBootstrapper() {
+ SpringApplication application = new SpringApplication(ExampleConfig.class);
+ application.setWebApplicationType(WebApplicationType.NONE);
+ application.addBootstrapper(
+ (bootstrapContext) -> bootstrapContext.register(String.class, InstanceSupplier.of("boot")));
+ TestApplicationListener listener = new TestApplicationListener();
+ application.addListeners(listener);
+ application.run();
+ ApplicationStartingEvent startingEvent = listener.getEvent(ApplicationStartingEvent.class);
+ assertThat(startingEvent.getBootstrapContext().get(String.class));
+ ApplicationEnvironmentPreparedEvent environmentPreparedEvent = listener
+ .getEvent(ApplicationEnvironmentPreparedEvent.class);
+ assertThat(environmentPreparedEvent.getBootstrapContext().get(String.class));
+ }
+
+ @Test
+ void addBootstrapperCanRegisterBeans() {
+ SpringApplication application = new SpringApplication(ExampleConfig.class);
+ application.setWebApplicationType(WebApplicationType.NONE);
+ application.addBootstrapper((bootstrapContext) -> {
+ bootstrapContext.register(String.class, InstanceSupplier.of("boot"));
+ bootstrapContext.addCloseListener((event) -> event.getApplicationContext().getBeanFactory()
+ .registerSingleton("test", event.getBootstrapContext().get(String.class)));
+ });
+ ConfigurableApplicationContext applicationContext = application.run();
+ assertThat(applicationContext.getBean("test")).isEqualTo("boot");
+ }
+
private ArgumentMatcher isAvailabilityChangeEventWithState(
S state) {
return (argument) -> (argument instanceof AvailabilityChangeEvent>)
@@ -1658,4 +1690,20 @@ class SpringApplicationTests {
}
+ static class TestApplicationListener implements ApplicationListener {
+
+ private final MultiValueMap, ApplicationEvent> events = new LinkedMultiValueMap<>();
+
+ @Override
+ public void onApplicationEvent(ApplicationEvent event) {
+ this.events.add(event.getClass(), event);
+ }
+
+ @SuppressWarnings("unchecked")
+ E getEvent(Class type) {
+ return (E) this.events.get(type).get(0);
+ }
+
+ }
+
}
diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/builder/SpringApplicationBuilderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/builder/SpringApplicationBuilderTests.java
index 43b657ddf87..de6139cb6b5 100644
--- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/builder/SpringApplicationBuilderTests.java
+++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/builder/SpringApplicationBuilderTests.java
@@ -282,6 +282,15 @@ class SpringApplicationBuilderTests {
this.context.getBean(ChildConfig.class);
}
+ @Test
+ void addBootstrapper() {
+ SpringApplicationBuilder application = new SpringApplicationBuilder(ExampleConfig.class)
+ .web(WebApplicationType.NONE).addBootstrapper((context) -> context.addCloseListener(
+ (event) -> event.getApplicationContext().getBeanFactory().registerSingleton("test", "spring")));
+ this.context = application.run();
+ assertThat(this.context.getBean("test")).isEqualTo("spring");
+ }
+
@Configuration(proxyBeanMethods = false)
static class ExampleConfig {
diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/ApplicationPidFileWriterTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/ApplicationPidFileWriterTests.java
index c4bd0600c22..5a62022ce55 100644
--- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/ApplicationPidFileWriterTests.java
+++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/ApplicationPidFileWriterTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2019 the original author or authors.
+ * Copyright 2012-2020 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.
@@ -25,6 +25,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
+import org.springframework.boot.DefaultBootstrapContext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.context.event.ApplicationPreparedEvent;
@@ -123,7 +124,8 @@ class ApplicationPidFileWriterTests {
File file = new File(this.tempDir, "pid");
ApplicationPidFileWriter listener = new ApplicationPidFileWriter(file);
listener.setTriggerEventType(ApplicationStartingEvent.class);
- listener.onApplicationEvent(new ApplicationStartingEvent(new SpringApplication(), new String[] {}));
+ listener.onApplicationEvent(
+ new ApplicationStartingEvent(new DefaultBootstrapContext(), new SpringApplication(), new String[] {}));
assertThat(contentOf(file)).isNotEmpty();
}
@@ -170,7 +172,8 @@ class ApplicationPidFileWriterTests {
private SpringApplicationEvent createEnvironmentPreparedEvent(String propName, String propValue) {
ConfigurableEnvironment environment = createEnvironment(propName, propValue);
- return new ApplicationEnvironmentPreparedEvent(new SpringApplication(), new String[] {}, environment);
+ return new ApplicationEnvironmentPreparedEvent(new DefaultBootstrapContext(), new SpringApplication(),
+ new String[] {}, environment);
}
private SpringApplicationEvent createPreparedEvent(String propName, String propValue) {
diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/FileEncodingApplicationListenerTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/FileEncodingApplicationListenerTests.java
index 0c70e535323..0a4a6f220c3 100644
--- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/FileEncodingApplicationListenerTests.java
+++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/FileEncodingApplicationListenerTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2019 the original author or authors.
+ * Copyright 2012-2020 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.
@@ -18,6 +18,7 @@ package org.springframework.boot.context;
import org.junit.jupiter.api.Test;
+import org.springframework.boot.DefaultBootstrapContext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
@@ -40,7 +41,7 @@ class FileEncodingApplicationListenerTests {
private final ConfigurableEnvironment environment = new StandardEnvironment();
private final ApplicationEnvironmentPreparedEvent event = new ApplicationEnvironmentPreparedEvent(
- new SpringApplication(), new String[0], this.environment);
+ new DefaultBootstrapContext(), new SpringApplication(), new String[0], this.environment);
@Test
void testIllegalState() {
diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorsTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorsTests.java
index a10fe120914..3225b68ecc8 100644
--- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorsTests.java
+++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorsTests.java
@@ -30,13 +30,12 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.boot.DefaultBootstrapContext;
import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.boot.context.config.ConfigDataEnvironmentContributor.Kind;
import org.springframework.boot.context.config.ConfigDataEnvironmentContributors.BinderOption;
import org.springframework.boot.context.properties.bind.BindException;
import org.springframework.boot.context.properties.bind.Binder;
-import org.springframework.boot.env.BootstrapRegistry;
-import org.springframework.boot.env.DefaultBootstrapRegisty;
import org.springframework.boot.logging.DeferredLogFactory;
import org.springframework.mock.env.MockEnvironment;
import org.springframework.mock.env.MockPropertySource;
@@ -60,7 +59,7 @@ class ConfigDataEnvironmentContributorsTests {
private DeferredLogFactory logFactory = Supplier::get;
- private BootstrapRegistry bootstrapRegistry = new DefaultBootstrapRegisty();
+ private DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
private MockEnvironment environment;
@@ -80,9 +79,10 @@ class ConfigDataEnvironmentContributorsTests {
void setup() {
this.environment = new MockEnvironment();
this.binder = Binder.get(this.environment);
- ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory,
+ ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext,
ConfigDataLocationNotFoundAction.FAIL, this.binder, null);
- ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, ConfigDataLocationNotFoundAction.FAIL);
+ ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, this.bootstrapContext,
+ ConfigDataLocationNotFoundAction.FAIL);
this.importer = new ConfigDataImporter(resolvers, loaders);
this.activationContext = new ConfigDataActivationContext(CloudPlatform.KUBERNETES, null);
}
@@ -91,7 +91,7 @@ class ConfigDataEnvironmentContributorsTests {
void createCreatesWithInitialContributors() {
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport("test");
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
- this.bootstrapRegistry, Arrays.asList(contributor));
+ this.bootstrapContext, Arrays.asList(contributor));
Iterator iterator = contributors.iterator();
assertThat(iterator.next()).isSameAs(contributor);
assertThat(iterator.next().getKind()).isEqualTo(Kind.ROOT);
@@ -102,7 +102,7 @@ class ConfigDataEnvironmentContributorsTests {
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor
.ofExisting(new MockPropertySource());
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
- this.bootstrapRegistry, Arrays.asList(contributor));
+ this.bootstrapContext, Arrays.asList(contributor));
ConfigDataEnvironmentContributors withProcessedImports = contributors.withProcessedImports(this.importer,
this.activationContext);
assertThat(withProcessedImports).isSameAs(contributors);
@@ -119,7 +119,7 @@ class ConfigDataEnvironmentContributorsTests {
.willReturn(imported);
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport("testimport");
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
- this.bootstrapRegistry, Arrays.asList(contributor));
+ this.bootstrapContext, Arrays.asList(contributor));
ConfigDataEnvironmentContributors withProcessedImports = contributors.withProcessedImports(this.importer,
this.activationContext);
Iterator iterator = withProcessedImports.iterator();
@@ -148,7 +148,7 @@ class ConfigDataEnvironmentContributorsTests {
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor
.ofInitialImport("initialimport");
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
- this.bootstrapRegistry, Arrays.asList(contributor));
+ this.bootstrapContext, Arrays.asList(contributor));
ConfigDataEnvironmentContributors withProcessedImports = contributors.withProcessedImports(this.importer,
this.activationContext);
Iterator iterator = withProcessedImports.iterator();
@@ -174,7 +174,7 @@ class ConfigDataEnvironmentContributorsTests {
.willReturn(imported);
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport("testimport");
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
- this.bootstrapRegistry, Arrays.asList(existingContributor, contributor));
+ this.bootstrapContext, Arrays.asList(existingContributor, contributor));
contributors.withProcessedImports(this.importer, this.activationContext);
verify(this.importer).resolveAndLoad(any(), this.locationResolverContext.capture(), any(), any());
ConfigDataLocationResolverContext context = this.locationResolverContext.getValue();
@@ -200,7 +200,7 @@ class ConfigDataEnvironmentContributorsTests {
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor
.ofInitialImport("initialimport");
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
- this.bootstrapRegistry, Arrays.asList(contributor));
+ this.bootstrapContext, Arrays.asList(contributor));
contributors.withProcessedImports(this.importer, this.activationContext);
verify(this.importer).resolveAndLoad(any(), this.locationResolverContext.capture(), any(), eq(secondLocations));
ConfigDataLocationResolverContext context = this.locationResolverContext.getValue();
@@ -222,11 +222,11 @@ class ConfigDataEnvironmentContributorsTests {
.willReturn(imported);
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport("testimport");
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
- this.bootstrapRegistry, Arrays.asList(existingContributor, contributor));
+ this.bootstrapContext, Arrays.asList(existingContributor, contributor));
contributors.withProcessedImports(this.importer, this.activationContext);
verify(this.importer).resolveAndLoad(any(), this.locationResolverContext.capture(), any(), any());
ConfigDataLocationResolverContext context = this.locationResolverContext.getValue();
- assertThat(context.getBootstrapRegistry()).isSameAs(this.bootstrapRegistry);
+ assertThat(context.getBootstrapContext()).isSameAs(this.bootstrapContext);
}
@Test
@@ -244,11 +244,11 @@ class ConfigDataEnvironmentContributorsTests {
.willReturn(imported);
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport("testimport");
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
- this.bootstrapRegistry, Arrays.asList(existingContributor, contributor));
+ this.bootstrapContext, Arrays.asList(existingContributor, contributor));
contributors.withProcessedImports(this.importer, this.activationContext);
verify(this.importer).resolveAndLoad(any(), any(), this.loaderContext.capture(), any());
ConfigDataLoaderContext context = this.loaderContext.getValue();
- assertThat(context.getBootstrapRegistry()).isSameAs(this.bootstrapRegistry);
+ assertThat(context.getBootstrapContext()).isSameAs(this.bootstrapContext);
}
@Test
@@ -257,7 +257,7 @@ class ConfigDataEnvironmentContributorsTests {
propertySource.setProperty("test", "springboot");
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofExisting(propertySource);
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
- this.bootstrapRegistry, Arrays.asList(contributor));
+ this.bootstrapContext, Arrays.asList(contributor));
Binder binder = contributors.getBinder(this.activationContext);
assertThat(binder.bind("test", String.class).get()).isEqualTo("springboot");
}
@@ -272,7 +272,7 @@ class ConfigDataEnvironmentContributorsTests {
ConfigDataEnvironmentContributor firstContributor = createBoundImportContributor(configData, 0);
ConfigDataEnvironmentContributor secondContributor = createBoundImportContributor(configData, 1);
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
- this.bootstrapRegistry, Arrays.asList(firstContributor, secondContributor));
+ this.bootstrapContext, Arrays.asList(firstContributor, secondContributor));
Binder binder = contributors.getBinder(this.activationContext);
assertThat(binder.bind("test", String.class).get()).isEqualTo("one");
}
@@ -288,7 +288,7 @@ class ConfigDataEnvironmentContributorsTests {
ConfigDataEnvironmentContributor firstContributor = createBoundImportContributor(configData, 0);
ConfigDataEnvironmentContributor secondContributor = createBoundImportContributor(configData, 1);
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
- this.bootstrapRegistry, Arrays.asList(firstContributor, secondContributor));
+ this.bootstrapContext, Arrays.asList(firstContributor, secondContributor));
Binder binder = contributors.getBinder(this.activationContext);
assertThat(binder.bind("test", String.class).get()).isEqualTo("two");
}
@@ -300,7 +300,7 @@ class ConfigDataEnvironmentContributorsTests {
propertySource.setProperty("other", "springboot");
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofExisting(propertySource);
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
- this.bootstrapRegistry, Arrays.asList(contributor));
+ this.bootstrapContext, Arrays.asList(contributor));
Binder binder = contributors.getBinder(this.activationContext);
assertThat(binder.bind("test", String.class).get()).isEqualTo("springboot");
}
@@ -317,7 +317,7 @@ class ConfigDataEnvironmentContributorsTests {
ConfigDataEnvironmentContributor firstContributor = createBoundImportContributor(configData, 0);
ConfigDataEnvironmentContributor secondContributor = createBoundImportContributor(configData, 1);
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
- this.bootstrapRegistry, Arrays.asList(firstContributor, secondContributor));
+ this.bootstrapContext, Arrays.asList(firstContributor, secondContributor));
Binder binder = contributors.getBinder(this.activationContext);
assertThat(binder.bind("test", String.class).get()).isEqualTo("two");
}
@@ -333,7 +333,7 @@ class ConfigDataEnvironmentContributorsTests {
ConfigDataEnvironmentContributor firstContributor = createBoundImportContributor(configData, 0);
ConfigDataEnvironmentContributor secondContributor = createBoundImportContributor(configData, 1);
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
- this.bootstrapRegistry, Arrays.asList(firstContributor, secondContributor));
+ this.bootstrapContext, Arrays.asList(firstContributor, secondContributor));
Binder binder = contributors.getBinder(this.activationContext, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE);
assertThatExceptionOfType(BindException.class).isThrownBy(() -> binder.bind("test", String.class))
.satisfies((ex) -> assertThat(ex.getCause()).isInstanceOf(InactiveConfigDataAccessException.class));
@@ -350,7 +350,7 @@ class ConfigDataEnvironmentContributorsTests {
ConfigDataEnvironmentContributor firstContributor = createBoundImportContributor(configData, 0);
ConfigDataEnvironmentContributor secondContributor = createBoundImportContributor(configData, 1);
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
- this.bootstrapRegistry, Arrays.asList(firstContributor, secondContributor));
+ this.bootstrapContext, Arrays.asList(firstContributor, secondContributor));
Binder binder = contributors.getBinder(this.activationContext, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE);
assertThatExceptionOfType(BindException.class).isThrownBy(() -> binder.bind("test", String.class))
.satisfies((ex) -> assertThat(ex.getCause()).isInstanceOf(InactiveConfigDataAccessException.class));
@@ -368,7 +368,7 @@ class ConfigDataEnvironmentContributorsTests {
ConfigDataEnvironmentContributor firstContributor = createBoundImportContributor(configData, 0);
ConfigDataEnvironmentContributor secondContributor = createBoundImportContributor(configData, 1);
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
- this.bootstrapRegistry, Arrays.asList(firstContributor, secondContributor));
+ this.bootstrapContext, Arrays.asList(firstContributor, secondContributor));
Binder binder = contributors.getBinder(this.activationContext, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE);
assertThatExceptionOfType(BindException.class).isThrownBy(() -> binder.bind("test", String.class))
.satisfies((ex) -> assertThat(ex.getCause()).isInstanceOf(InactiveConfigDataAccessException.class));
diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorBootstrapRegistryIntegrationTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorBootstrapRegistryIntegrationTests.java
index 9c0b375a963..bf6a8a3d0ff 100644
--- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorBootstrapRegistryIntegrationTests.java
+++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorBootstrapRegistryIntegrationTests.java
@@ -19,10 +19,10 @@ package org.springframework.boot.context.config;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.springframework.boot.BootstrapRegistry;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.context.config.TestConfigDataBootstrap.LoaderHelper;
-import org.springframework.boot.env.BootstrapRegistry;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorTests.java
index 01d0278059e..fbf67a693c8 100644
--- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorTests.java
+++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorTests.java
@@ -27,8 +27,8 @@ import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.boot.DefaultBootstrapContext;
import org.springframework.boot.SpringApplication;
-import org.springframework.boot.env.DefaultBootstrapRegisty;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.ResourceLoader;
@@ -60,7 +60,7 @@ class ConfigDataEnvironmentPostProcessorTests {
@Spy
private ConfigDataEnvironmentPostProcessor postProcessor = new ConfigDataEnvironmentPostProcessor(Supplier::get,
- new DefaultBootstrapRegisty());
+ new DefaultBootstrapContext());
@Captor
private ArgumentCaptor> additionalProfilesCaptor;
diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentTests.java
index 63b11f4abd7..be14fb81b38 100644
--- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentTests.java
+++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentTests.java
@@ -26,11 +26,11 @@ import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
+import org.springframework.boot.ConfigurableBootstrapContext;
+import org.springframework.boot.DefaultBootstrapContext;
import org.springframework.boot.context.config.ConfigDataEnvironmentContributor.ImportPhase;
import org.springframework.boot.context.config.ConfigDataEnvironmentContributor.Kind;
import org.springframework.boot.context.properties.bind.Binder;
-import org.springframework.boot.env.BootstrapRegistry;
-import org.springframework.boot.env.DefaultBootstrapRegisty;
import org.springframework.boot.logging.DeferredLogFactory;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource;
@@ -52,7 +52,7 @@ class ConfigDataEnvironmentTests {
private DeferredLogFactory logFactory = Supplier::get;
- private BootstrapRegistry bootstrapRegistry = new DefaultBootstrapRegisty();
+ private DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
private MockEnvironment environment = new MockEnvironment();
@@ -64,7 +64,7 @@ class ConfigDataEnvironmentTests {
void createWhenUseLegacyPropertyInEnvironmentThrowsException() {
this.environment.setProperty("spring.config.use-legacy-processing", "true");
assertThatExceptionOfType(UseLegacyConfigProcessingException.class)
- .isThrownBy(() -> new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry, this.environment,
+ .isThrownBy(() -> new ConfigDataEnvironment(this.logFactory, this.bootstrapContext, this.environment,
this.resourceLoader, this.additionalProfiles));
}
@@ -72,7 +72,7 @@ class ConfigDataEnvironmentTests {
void createExposesEnvironmentBinderToConfigDataLocationResolvers() {
this.environment.setProperty("spring", "boot");
TestConfigDataEnvironment configDataEnvironment = new TestConfigDataEnvironment(this.logFactory,
- this.bootstrapRegistry, this.environment, this.resourceLoader, this.additionalProfiles);
+ this.bootstrapContext, this.environment, this.resourceLoader, this.additionalProfiles);
assertThat(configDataEnvironment.getConfigDataLocationResolversBinder().bind("spring", String.class).get())
.isEqualTo("boot");
}
@@ -85,7 +85,7 @@ class ConfigDataEnvironmentTests {
this.environment.getPropertySources().addLast(propertySource1);
this.environment.getPropertySources().addLast(propertySource2);
this.environment.getPropertySources().addLast(propertySource3);
- ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry,
+ ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapContext,
this.environment, this.resourceLoader, this.additionalProfiles);
List children = configDataEnvironment.getContributors().getRoot()
.getChildren(ImportPhase.BEFORE_PROFILE_ACTIVATION);
@@ -104,7 +104,7 @@ class ConfigDataEnvironmentTests {
this.environment.getPropertySources().addLast(defaultPropertySource);
this.environment.getPropertySources().addLast(propertySource1);
this.environment.getPropertySources().addLast(propertySource2);
- ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry,
+ ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapContext,
this.environment, this.resourceLoader, this.additionalProfiles);
List children = configDataEnvironment.getContributors().getRoot()
.getChildren(ImportPhase.BEFORE_PROFILE_ACTIVATION);
@@ -120,7 +120,7 @@ class ConfigDataEnvironmentTests {
this.environment.setProperty("spring.config.location", "l1,l2");
this.environment.setProperty("spring.config.additional-location", "a1,a2");
this.environment.setProperty("spring.config.import", "i1,i2");
- ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry,
+ ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapContext,
this.environment, this.resourceLoader, this.additionalProfiles);
List children = configDataEnvironment.getContributors().getRoot()
.getChildren(ImportPhase.BEFORE_PROFILE_ACTIVATION);
@@ -132,7 +132,7 @@ class ConfigDataEnvironmentTests {
@Test
void processAndApplyAddsImportedSourceToEnvironment(TestInfo info) {
this.environment.setProperty("spring.config.location", getConfigLocation(info));
- ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry,
+ ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapContext,
this.environment, this.resourceLoader, this.additionalProfiles);
configDataEnvironment.processAndApply();
assertThat(this.environment.getProperty("spring")).isEqualTo("boot");
@@ -141,7 +141,7 @@ class ConfigDataEnvironmentTests {
@Test
void processAndApplyOnlyAddsActiveContributors(TestInfo info) {
this.environment.setProperty("spring.config.location", getConfigLocation(info));
- ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry,
+ ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapContext,
this.environment, this.resourceLoader, this.additionalProfiles);
configDataEnvironment.processAndApply();
assertThat(this.environment.getProperty("spring")).isEqualTo("boot");
@@ -153,7 +153,7 @@ class ConfigDataEnvironmentTests {
MockPropertySource defaultPropertySource = new MockPropertySource("defaultProperties");
this.environment.getPropertySources().addFirst(defaultPropertySource);
this.environment.setProperty("spring.config.location", getConfigLocation(info));
- ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry,
+ ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapContext,
this.environment, this.resourceLoader, this.additionalProfiles);
configDataEnvironment.processAndApply();
List> sources = this.environment.getPropertySources().stream().collect(Collectors.toList());
@@ -163,7 +163,7 @@ class ConfigDataEnvironmentTests {
@Test
void processAndApplySetsDefaultProfiles(TestInfo info) {
this.environment.setProperty("spring.config.location", getConfigLocation(info));
- ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry,
+ ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapContext,
this.environment, this.resourceLoader, this.additionalProfiles);
configDataEnvironment.processAndApply();
assertThat(this.environment.getDefaultProfiles()).containsExactly("one", "two", "three");
@@ -172,7 +172,7 @@ class ConfigDataEnvironmentTests {
@Test
void processAndApplySetsActiveProfiles(TestInfo info) {
this.environment.setProperty("spring.config.location", getConfigLocation(info));
- ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry,
+ ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapContext,
this.environment, this.resourceLoader, this.additionalProfiles);
configDataEnvironment.processAndApply();
assertThat(this.environment.getActiveProfiles()).containsExactly("one", "two", "three");
@@ -181,7 +181,7 @@ class ConfigDataEnvironmentTests {
@Test
void processAndApplySetsActiveProfilesAndProfileGroups(TestInfo info) {
this.environment.setProperty("spring.config.location", getConfigLocation(info));
- ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry,
+ ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapContext,
this.environment, this.resourceLoader, this.additionalProfiles);
configDataEnvironment.processAndApply();
assertThat(this.environment.getActiveProfiles()).containsExactly("one", "four", "five", "two", "three");
@@ -191,7 +191,7 @@ class ConfigDataEnvironmentTests {
@Disabled("Disabled until spring.profiles support is dropped")
void processAndApplyWhenHasInvalidPropertyThrowsException() {
this.environment.setProperty("spring.profile", "a");
- ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry,
+ ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapContext,
this.environment, this.resourceLoader, this.additionalProfiles);
assertThatExceptionOfType(InvalidConfigDataPropertyException.class)
.isThrownBy(() -> configDataEnvironment.processAndApply());
@@ -206,17 +206,19 @@ class ConfigDataEnvironmentTests {
private Binder configDataLocationResolversBinder;
- TestConfigDataEnvironment(DeferredLogFactory logFactory, BootstrapRegistry bootstrapRegistry,
+ TestConfigDataEnvironment(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext,
ConfigurableEnvironment environment, ResourceLoader resourceLoader,
Collection additionalProfiles) {
- super(logFactory, bootstrapRegistry, environment, resourceLoader, additionalProfiles);
+ super(logFactory, bootstrapContext, environment, resourceLoader, additionalProfiles);
}
@Override
protected ConfigDataLocationResolvers createConfigDataLocationResolvers(DeferredLogFactory logFactory,
- ConfigDataLocationNotFoundAction locationNotFoundAction, Binder binder, ResourceLoader resourceLoader) {
+ ConfigurableBootstrapContext bootstrapContext, ConfigDataLocationNotFoundAction locationNotFoundAction,
+ Binder binder, ResourceLoader resourceLoader) {
this.configDataLocationResolversBinder = binder;
- return super.createConfigDataLocationResolvers(logFactory, locationNotFoundAction, binder, resourceLoader);
+ return super.createConfigDataLocationResolvers(logFactory, bootstrapContext, locationNotFoundAction, binder,
+ resourceLoader);
}
Binder getConfigDataLocationResolversBinder() {
diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLoadersTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLoadersTests.java
index b1e3b0f88b1..f69339508e1 100644
--- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLoadersTests.java
+++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLoadersTests.java
@@ -24,6 +24,11 @@ import java.util.function.Supplier;
import org.apache.commons.logging.Log;
import org.junit.jupiter.api.Test;
+import org.springframework.boot.BootstrapContext;
+import org.springframework.boot.BootstrapRegistry;
+import org.springframework.boot.BootstrapRegistry.InstanceSupplier;
+import org.springframework.boot.ConfigurableBootstrapContext;
+import org.springframework.boot.DefaultBootstrapContext;
import org.springframework.boot.logging.DeferredLogFactory;
import org.springframework.core.env.PropertySource;
import org.springframework.mock.env.MockPropertySource;
@@ -42,19 +47,28 @@ class ConfigDataLoadersTests {
private DeferredLogFactory logFactory = Supplier::get;
+ private DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
+
private ConfigDataLoaderContext context = mock(ConfigDataLoaderContext.class);
@Test
void createWhenLoaderHasLogParameterInjectsLog() {
- new ConfigDataLoaders(this.logFactory, ConfigDataLocationNotFoundAction.FAIL,
+ new ConfigDataLoaders(this.logFactory, this.bootstrapContext, ConfigDataLocationNotFoundAction.FAIL,
Arrays.asList(LoggingConfigDataLoader.class.getName()));
}
+ @Test
+ void createWhenLoaderHasBootstrapParametersInjectsBootstrapContext() {
+ new ConfigDataLoaders(this.logFactory, this.bootstrapContext, ConfigDataLocationNotFoundAction.FAIL,
+ Arrays.asList(BootstrappingConfigDataLoader.class.getName()));
+ assertThat(this.bootstrapContext.get(String.class)).isEqualTo("boot");
+ }
+
@Test
void loadWhenSingleLoaderSupportsLocationReturnsLoadedConfigData() throws Exception {
TestConfigDataLocation location = new TestConfigDataLocation("test");
- ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, ConfigDataLocationNotFoundAction.FAIL,
- Arrays.asList(TestConfigDataLoader.class.getName()));
+ ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, this.bootstrapContext,
+ ConfigDataLocationNotFoundAction.FAIL, Arrays.asList(TestConfigDataLoader.class.getName()));
ConfigData loaded = loaders.load(this.context, location);
assertThat(getLoader(loaded)).isInstanceOf(TestConfigDataLoader.class);
}
@@ -62,7 +76,8 @@ class ConfigDataLoadersTests {
@Test
void loadWhenMultipleLoadersSupportLocationThrowsException() throws Exception {
TestConfigDataLocation location = new TestConfigDataLocation("test");
- ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, ConfigDataLocationNotFoundAction.FAIL,
+ ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, this.bootstrapContext,
+ ConfigDataLocationNotFoundAction.FAIL,
Arrays.asList(LoggingConfigDataLoader.class.getName(), TestConfigDataLoader.class.getName()));
assertThatIllegalStateException().isThrownBy(() -> loaders.load(this.context, location))
.withMessageContaining("Multiple loaders found for location test");
@@ -71,8 +86,8 @@ class ConfigDataLoadersTests {
@Test
void loadWhenNoLoaderSupportsLocationThrowsException() {
TestConfigDataLocation location = new TestConfigDataLocation("test");
- ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, ConfigDataLocationNotFoundAction.FAIL,
- Arrays.asList(NonLoadableConfigDataLoader.class.getName()));
+ ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, this.bootstrapContext,
+ ConfigDataLocationNotFoundAction.FAIL, Arrays.asList(NonLoadableConfigDataLoader.class.getName()));
assertThatIllegalStateException().isThrownBy(() -> loaders.load(this.context, location))
.withMessage("No loader found for location 'test'");
}
@@ -80,7 +95,8 @@ class ConfigDataLoadersTests {
@Test
void loadWhenGenericTypeDoesNotMatchSkipsLoader() throws Exception {
TestConfigDataLocation location = new TestConfigDataLocation("test");
- ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, ConfigDataLocationNotFoundAction.FAIL,
+ ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, this.bootstrapContext,
+ ConfigDataLocationNotFoundAction.FAIL,
Arrays.asList(OtherConfigDataLoader.class.getName(), SpecificConfigDataLoader.class.getName()));
ConfigData loaded = loaders.load(this.context, location);
assertThat(getLoader(loaded)).isInstanceOf(SpecificConfigDataLoader.class);
@@ -131,6 +147,24 @@ class ConfigDataLoadersTests {
}
+ static class BootstrappingConfigDataLoader implements ConfigDataLoader {
+
+ BootstrappingConfigDataLoader(ConfigurableBootstrapContext configurableBootstrapContext,
+ BootstrapRegistry bootstrapRegistry, BootstrapContext bootstrapContext) {
+ assertThat(configurableBootstrapContext).isNotNull();
+ assertThat(bootstrapRegistry).isNotNull();
+ assertThat(bootstrapContext).isNotNull();
+ assertThat(configurableBootstrapContext).isEqualTo(bootstrapRegistry).isEqualTo(bootstrapContext);
+ bootstrapRegistry.register(String.class, InstanceSupplier.of("boot"));
+ }
+
+ @Override
+ public ConfigData load(ConfigDataLoaderContext context, ConfigDataLocation location) throws IOException {
+ throw new AssertionError("Unexpected call");
+ }
+
+ }
+
static class TestConfigDataLoader implements ConfigDataLoader {
@Override
diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLocationResolversTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLocationResolversTests.java
index f699860bd8f..7e0171be6a9 100644
--- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLocationResolversTests.java
+++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLocationResolversTests.java
@@ -28,6 +28,11 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.boot.BootstrapContext;
+import org.springframework.boot.BootstrapRegistry;
+import org.springframework.boot.BootstrapRegistry.InstanceSupplier;
+import org.springframework.boot.ConfigurableBootstrapContext;
+import org.springframework.boot.DefaultBootstrapContext;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.logging.DeferredLogFactory;
import org.springframework.core.Ordered;
@@ -50,6 +55,8 @@ class ConfigDataLocationResolversTests {
private DeferredLogFactory logFactory = Supplier::get;
+ private DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
+
@Mock
private Binder binder;
@@ -63,7 +70,7 @@ class ConfigDataLocationResolversTests {
@Test
void createWhenInjectingBinderCreatesResolver() {
- ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory,
+ ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext,
ConfigDataLocationNotFoundAction.FAIL, this.binder, this.resourceLoader,
Collections.singletonList(TestBoundResolver.class.getName()));
assertThat(resolvers.getResolvers()).hasSize(1);
@@ -73,17 +80,24 @@ class ConfigDataLocationResolversTests {
@Test
void createWhenNotInjectingBinderCreatesResolver() {
- ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory,
+ ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext,
ConfigDataLocationNotFoundAction.FAIL, this.binder, this.resourceLoader,
Collections.singletonList(TestResolver.class.getName()));
assertThat(resolvers.getResolvers()).hasSize(1);
assertThat(resolvers.getResolvers().get(0)).isExactlyInstanceOf(TestResolver.class);
}
+ @Test
+ void createWhenResolverHasBootstrapParametersInjectsBootstrapContext() {
+ new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext, ConfigDataLocationNotFoundAction.FAIL,
+ this.binder, this.resourceLoader, Collections.singletonList(TestBootstrappingResolver.class.getName()));
+ assertThat(this.bootstrapContext.get(String.class)).isEqualTo("boot");
+ }
+
@Test
void createWhenNameIsNotConfigDataLocationResolverThrowsException() {
assertThatIllegalArgumentException()
- .isThrownBy(() -> new ConfigDataLocationResolvers(this.logFactory,
+ .isThrownBy(() -> new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext,
ConfigDataLocationNotFoundAction.FAIL, this.binder, this.resourceLoader,
Collections.singletonList(InputStream.class.getName())))
.withMessageContaining("Unable to instantiate").havingCause().withMessageContaining("not assignable");
@@ -95,7 +109,7 @@ class ConfigDataLocationResolversTests {
names.add(TestResolver.class.getName());
names.add(LowestTestResolver.class.getName());
names.add(HighestTestResolver.class.getName());
- ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory,
+ ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext,
ConfigDataLocationNotFoundAction.FAIL, this.binder, this.resourceLoader, names);
assertThat(resolvers.getResolvers().get(0)).isExactlyInstanceOf(HighestTestResolver.class);
assertThat(resolvers.getResolvers().get(1)).isExactlyInstanceOf(TestResolver.class);
@@ -104,7 +118,7 @@ class ConfigDataLocationResolversTests {
@Test
void resolveAllResolvesUsingFirstSupportedResolver() {
- ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory,
+ ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext,
ConfigDataLocationNotFoundAction.FAIL, this.binder, this.resourceLoader,
Arrays.asList(LowestTestResolver.class.getName(), HighestTestResolver.class.getName()));
List resolved = resolvers.resolveAll(this.context,
@@ -118,7 +132,7 @@ class ConfigDataLocationResolversTests {
@Test
void resolveAllWhenProfileMergesResolvedLocations() {
- ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory,
+ ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext,
ConfigDataLocationNotFoundAction.FAIL, this.binder, this.resourceLoader,
Arrays.asList(LowestTestResolver.class.getName(), HighestTestResolver.class.getName()));
List resolved = resolvers.resolveAll(this.context,
@@ -136,7 +150,7 @@ class ConfigDataLocationResolversTests {
@Test
void resolveWhenNoResolverThrowsException() {
- ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory,
+ ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext,
ConfigDataLocationNotFoundAction.FAIL, this.binder, this.resourceLoader,
Arrays.asList(LowestTestResolver.class.getName(), HighestTestResolver.class.getName()));
assertThatExceptionOfType(UnsupportedConfigDataLocationException.class)
@@ -181,6 +195,19 @@ class ConfigDataLocationResolversTests {
}
+ static class TestBootstrappingResolver extends TestResolver {
+
+ TestBootstrappingResolver(ConfigurableBootstrapContext configurableBootstrapContext,
+ BootstrapRegistry bootstrapRegistry, BootstrapContext bootstrapContext) {
+ assertThat(configurableBootstrapContext).isNotNull();
+ assertThat(bootstrapRegistry).isNotNull();
+ assertThat(bootstrapContext).isNotNull();
+ assertThat(configurableBootstrapContext).isEqualTo(bootstrapRegistry).isEqualTo(bootstrapContext);
+ bootstrapRegistry.register(String.class, InstanceSupplier.of("boot"));
+ }
+
+ }
+
@Order(Ordered.HIGHEST_PRECEDENCE)
static class HighestTestResolver extends TestResolver {
diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/DelegatingApplicationListenerTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/DelegatingApplicationListenerTests.java
index 4859df83d78..61cf3b45e07 100644
--- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/DelegatingApplicationListenerTests.java
+++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/DelegatingApplicationListenerTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2019 the original author or authors.
+ * Copyright 2012-2020 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.
@@ -19,6 +19,7 @@ package org.springframework.boot.context.config;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
+import org.springframework.boot.DefaultBootstrapContext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.context.ApplicationListener;
@@ -53,8 +54,8 @@ class DelegatingApplicationListenerTests {
void orderedInitialize() {
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context,
"context.listener.classes=" + MockInitB.class.getName() + "," + MockInitA.class.getName());
- this.listener.onApplicationEvent(new ApplicationEnvironmentPreparedEvent(new SpringApplication(), new String[0],
- this.context.getEnvironment()));
+ this.listener.onApplicationEvent(new ApplicationEnvironmentPreparedEvent(new DefaultBootstrapContext(),
+ new SpringApplication(), new String[0], this.context.getEnvironment()));
this.context.getBeanFactory().registerSingleton("testListener", this.listener);
this.context.refresh();
assertThat(this.context.getBeanFactory().getSingleton("a")).isEqualTo("a");
@@ -63,15 +64,15 @@ class DelegatingApplicationListenerTests {
@Test
void noInitializers() {
- this.listener.onApplicationEvent(new ApplicationEnvironmentPreparedEvent(new SpringApplication(), new String[0],
- this.context.getEnvironment()));
+ this.listener.onApplicationEvent(new ApplicationEnvironmentPreparedEvent(new DefaultBootstrapContext(),
+ new SpringApplication(), new String[0], this.context.getEnvironment()));
}
@Test
void emptyInitializers() {
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context, "context.listener.classes:");
- this.listener.onApplicationEvent(new ApplicationEnvironmentPreparedEvent(new SpringApplication(), new String[0],
- this.context.getEnvironment()));
+ this.listener.onApplicationEvent(new ApplicationEnvironmentPreparedEvent(new DefaultBootstrapContext(),
+ new SpringApplication(), new String[0], this.context.getEnvironment()));
}
@Order(Ordered.HIGHEST_PRECEDENCE)
diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/TestConfigDataBootstrap.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/TestConfigDataBootstrap.java
index cb739a1b1cb..6e0abfcc731 100644
--- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/TestConfigDataBootstrap.java
+++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/TestConfigDataBootstrap.java
@@ -20,7 +20,9 @@ import java.io.IOException;
import java.util.Collections;
import java.util.List;
-import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.boot.BootstrapContextClosedEvent;
+import org.springframework.boot.BootstrapRegistry.InstanceSupplier;
+import org.springframework.context.ApplicationListener;
import org.springframework.core.env.MapPropertySource;
/**
@@ -42,8 +44,9 @@ class TestConfigDataBootstrap {
@Override
public List resolve(ConfigDataLocationResolverContext context, String location, boolean optional) {
- ResolverHelper helper = context.getBootstrapRegistry().get(ResolverHelper.class,
- () -> new ResolverHelper(location));
+ context.getBootstrapContext().registerIfAbsent(ResolverHelper.class,
+ InstanceSupplier.from(() -> new ResolverHelper(location)));
+ ResolverHelper helper = context.getBootstrapContext().get(ResolverHelper.class);
return Collections.singletonList(new Location(helper));
}
@@ -53,8 +56,10 @@ class TestConfigDataBootstrap {
@Override
public ConfigData load(ConfigDataLoaderContext context, Location location) throws IOException {
- context.getBootstrapRegistry().get(LoaderHelper.class, () -> new LoaderHelper(location),
- LoaderHelper::addToContext);
+ context.getBootstrapContext().registerIfAbsent(LoaderHelper.class,
+ InstanceSupplier.from(() -> new LoaderHelper(location)));
+ LoaderHelper helper = context.getBootstrapContext().get(LoaderHelper.class);
+ context.getBootstrapContext().addCloseListener(helper);
return new ConfigData(
Collections.singleton(new MapPropertySource("loaded", Collections.singletonMap("test", "test"))));
}
@@ -94,7 +99,7 @@ class TestConfigDataBootstrap {
}
- static class LoaderHelper {
+ static class LoaderHelper implements ApplicationListener {
private final Location location;
@@ -106,8 +111,9 @@ class TestConfigDataBootstrap {
return this.location;
}
- static void addToContext(ConfigurableApplicationContext context, LoaderHelper loaderHelper) {
- context.getBeanFactory().registerSingleton("loaderHelper", loaderHelper);
+ @Override
+ public void onApplicationEvent(BootstrapContextClosedEvent event) {
+ event.getApplicationContext().getBeanFactory().registerSingleton("loaderHelper", this);
}
}
diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/event/EventPublishingRunListenerTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/event/EventPublishingRunListenerTests.java
index 877dd4f5e77..5f922fbbd71 100644
--- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/event/EventPublishingRunListenerTests.java
+++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/event/EventPublishingRunListenerTests.java
@@ -25,6 +25,7 @@ import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.springframework.boot.DefaultBootstrapContext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.availability.AvailabilityChangeEvent;
import org.springframework.context.ApplicationEvent;
@@ -42,6 +43,8 @@ import static org.mockito.Mockito.mock;
*/
class EventPublishingRunListenerTests {
+ private DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
+
private SpringApplication application;
private EventPublishingRunListener runListener;
@@ -60,9 +63,9 @@ class EventPublishingRunListenerTests {
void shouldPublishLifecycleEvents() {
StaticApplicationContext context = new StaticApplicationContext();
assertThat(this.eventListener.receivedEvents()).isEmpty();
- this.runListener.starting();
+ this.runListener.starting(this.bootstrapContext);
checkApplicationEvents(ApplicationStartingEvent.class);
- this.runListener.environmentPrepared(null);
+ this.runListener.environmentPrepared(this.bootstrapContext, null);
checkApplicationEvents(ApplicationEnvironmentPreparedEvent.class);
this.runListener.contextPrepared(context);
checkApplicationEvents(ApplicationContextInitializedEvent.class);
diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/logging/LoggingApplicationListenerTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/logging/LoggingApplicationListenerTests.java
index dadde0096cf..f72c008d3ea 100644
--- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/logging/LoggingApplicationListenerTests.java
+++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/logging/LoggingApplicationListenerTests.java
@@ -41,6 +41,7 @@ import org.junit.jupiter.api.io.TempDir;
import org.slf4j.bridge.SLF4JBridgeHandler;
import org.slf4j.impl.StaticLoggerBinder;
+import org.springframework.boot.DefaultBootstrapContext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.event.ApplicationFailedEvent;
import org.springframework.boot.context.event.ApplicationStartingEvent;
@@ -97,6 +98,8 @@ class LoggingApplicationListenerTests {
private final ch.qos.logback.classic.Logger logger = this.loggerContext.getLogger(getClass());
+ private final DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
+
private final SpringApplication springApplication = new SpringApplication();
private final GenericApplicationContext context = new GenericApplicationContext();
@@ -113,7 +116,7 @@ class LoggingApplicationListenerTests {
this.output = output;
this.logFile = new File(this.tempDir.toFile(), "foo.log");
LogManager.getLogManager().readConfiguration(JavaLoggingSystem.class.getResourceAsStream("logging.properties"));
- multicastEvent(new ApplicationStartingEvent(new SpringApplication(), NO_ARGS));
+ multicastEvent(new ApplicationStartingEvent(this.bootstrapContext, new SpringApplication(), NO_ARGS));
new File(this.tempDir.toFile(), "spring.log").delete();
ConfigurableEnvironment environment = this.context.getEnvironment();
ConfigurationPropertySources.attach(environment);
@@ -372,7 +375,8 @@ class LoggingApplicationListenerTests {
void parseArgsDoesntReplace() {
this.initializer.setSpringBootLogging(LogLevel.ERROR);
this.initializer.setParseArgs(false);
- multicastEvent(new ApplicationStartingEvent(this.springApplication, new String[] { "--debug" }));
+ multicastEvent(new ApplicationStartingEvent(this.bootstrapContext, this.springApplication,
+ new String[] { "--debug" }));
this.initializer.initialize(this.context.getEnvironment(), this.context.getClassLoader());
this.logger.debug("testatdebug");
assertThat(this.output).doesNotContain("testatdebug");
@@ -406,7 +410,7 @@ class LoggingApplicationListenerTests {
void shutdownHookIsNotRegisteredByDefault() {
TestLoggingApplicationListener listener = new TestLoggingApplicationListener();
System.setProperty(LoggingSystem.class.getName(), TestShutdownHandlerLoggingSystem.class.getName());
- multicastEvent(listener, new ApplicationStartingEvent(new SpringApplication(), NO_ARGS));
+ multicastEvent(listener, new ApplicationStartingEvent(this.bootstrapContext, new SpringApplication(), NO_ARGS));
listener.initialize(this.context.getEnvironment(), this.context.getClassLoader());
assertThat(listener.shutdownHook).isNull();
}
@@ -416,7 +420,7 @@ class LoggingApplicationListenerTests {
TestLoggingApplicationListener listener = new TestLoggingApplicationListener();
System.setProperty(LoggingSystem.class.getName(), TestShutdownHandlerLoggingSystem.class.getName());
addPropertiesToEnvironment(this.context, "logging.register_shutdown_hook=true");
- multicastEvent(listener, new ApplicationStartingEvent(new SpringApplication(), NO_ARGS));
+ multicastEvent(listener, new ApplicationStartingEvent(this.bootstrapContext, new SpringApplication(), NO_ARGS));
listener.initialize(this.context.getEnvironment(), this.context.getClassLoader());
assertThat(listener.shutdownHook).isNotNull();
listener.shutdownHook.start();
@@ -426,7 +430,7 @@ class LoggingApplicationListenerTests {
@Test
void closingContextCleansUpLoggingSystem() {
System.setProperty(LoggingSystem.SYSTEM_PROPERTY, TestCleanupLoggingSystem.class.getName());
- multicastEvent(new ApplicationStartingEvent(this.springApplication, new String[0]));
+ multicastEvent(new ApplicationStartingEvent(this.bootstrapContext, this.springApplication, new String[0]));
TestCleanupLoggingSystem loggingSystem = (TestCleanupLoggingSystem) ReflectionTestUtils
.getField(this.initializer, "loggingSystem");
assertThat(loggingSystem.cleanedUp).isFalse();
@@ -437,7 +441,7 @@ class LoggingApplicationListenerTests {
@Test
void closingChildContextDoesNotCleanUpLoggingSystem() {
System.setProperty(LoggingSystem.SYSTEM_PROPERTY, TestCleanupLoggingSystem.class.getName());
- multicastEvent(new ApplicationStartingEvent(this.springApplication, new String[0]));
+ multicastEvent(new ApplicationStartingEvent(this.bootstrapContext, this.springApplication, new String[0]));
TestCleanupLoggingSystem loggingSystem = (TestCleanupLoggingSystem) ReflectionTestUtils
.getField(this.initializer, "loggingSystem");
assertThat(loggingSystem.cleanedUp).isFalse();
@@ -496,7 +500,7 @@ class LoggingApplicationListenerTests {
@Test
void applicationFailedEventCleansUpLoggingSystem() {
System.setProperty(LoggingSystem.SYSTEM_PROPERTY, TestCleanupLoggingSystem.class.getName());
- multicastEvent(new ApplicationStartingEvent(this.springApplication, new String[0]));
+ multicastEvent(new ApplicationStartingEvent(this.bootstrapContext, this.springApplication, new String[0]));
TestCleanupLoggingSystem loggingSystem = (TestCleanupLoggingSystem) ReflectionTestUtils
.getField(this.initializer, "loggingSystem");
assertThat(loggingSystem.cleanedUp).isFalse();
diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/DefaultBootstrapRegistyTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/DefaultBootstrapRegistyTests.java
deleted file mode 100644
index b4faf99c12a..00000000000
--- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/DefaultBootstrapRegistyTests.java
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Copyright 2012-2020 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.env;
-
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.assertj.core.api.AbstractAssert;
-import org.assertj.core.api.AssertProvider;
-import org.junit.jupiter.api.Test;
-
-import org.springframework.boot.env.BootstrapRegistry.Registration;
-import org.springframework.context.ConfigurableApplicationContext;
-import org.springframework.context.support.StaticApplicationContext;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * Test for {@link DefaultBootstrapRegisty}.
- *
- * @author Phillip Webb
- */
-class DefaultBootstrapRegistyTests {
-
- private DefaultBootstrapRegisty registy = new DefaultBootstrapRegisty();
-
- private AtomicInteger counter = new AtomicInteger();
-
- private StaticApplicationContext context = new StaticApplicationContext();
-
- @Test
- void getWhenNotRegisteredCreateInstance() {
- Integer result = this.registy.get(Integer.class, this.counter::getAndIncrement);
- assertThat(result).isEqualTo(0);
- }
-
- @Test
- void getWhenAlreadyRegisteredReturnsExisting() {
- this.registy.get(Integer.class, this.counter::getAndIncrement);
- Integer result = this.registy.get(Integer.class, this.counter::getAndIncrement);
- assertThat(result).isEqualTo(0);
- }
-
- @Test
- void getWithPreparedActionRegistersAction() {
- TestApplicationPreparedAction action = new TestApplicationPreparedAction();
- Integer result = this.registy.get(Integer.class, this.counter::getAndIncrement, action::run);
- this.registy.applicationContextPrepared(this.context);
- assertThat(result).isEqualTo(0);
- assertThat(action).wasCalledOnlyOnce().hasInstanceValue(0);
- }
-
- @Test
- void getWithPreparedActionWhenAlreadyRegisteredIgnoresRegistersAction() {
- TestApplicationPreparedAction action1 = new TestApplicationPreparedAction();
- TestApplicationPreparedAction action2 = new TestApplicationPreparedAction();
- this.registy.get(Integer.class, this.counter::getAndIncrement, action1::run);
- this.registy.get(Integer.class, this.counter::getAndIncrement, action2::run);
- this.registy.applicationContextPrepared(this.context);
- assertThat(action1).wasCalledOnlyOnce().hasInstanceValue(0);
- assertThat(action2).wasNotCalled();
- }
-
- @Test
- void registerAddsRegistration() {
- Registration registration = this.registy.register(Integer.class, this.counter::getAndIncrement);
- assertThat(registration.get()).isEqualTo(0);
- }
-
- @Test
- void registerWhenAlreadyRegisteredReplacesPreviousRegistration() {
- Registration registration1 = this.registy.register(Integer.class, this.counter::getAndIncrement);
- Registration registration2 = this.registy.register(Integer.class, () -> -1);
- assertThat(registration2).isNotEqualTo(registration1);
- assertThat(registration1.get()).isEqualTo(0);
- assertThat(registration2.get()).isEqualTo(-1);
- assertThat(this.registy.get(Integer.class, this.counter::getAndIncrement)).isEqualTo(-1);
- }
-
- @Test
- void isRegisteredWhenNotRegisteredReturnsFalse() {
- assertThat(this.registy.isRegistered(Integer.class)).isFalse();
- }
-
- @Test
- void isRegisteredWhenRegisteredReturnsTrue() {
- this.registy.register(Integer.class, this.counter::getAndIncrement);
- assertThat(this.registy.isRegistered(Integer.class)).isTrue();
- }
-
- @Test
- void getRegistrationWhenNotRegisteredReturnsNull() {
- assertThat(this.registy.getRegistration(Integer.class)).isNull();
- }
-
- @Test
- void getRegistrationWhenRegisteredReturnsRegistration() {
- Registration registration = this.registy.register(Integer.class, this.counter::getAndIncrement);
- assertThat(this.registy.getRegistration(Integer.class)).isSameAs(registration);
- }
-
- @Test
- void applicationContextPreparedTriggersActions() {
- TestApplicationPreparedAction action1 = new TestApplicationPreparedAction();
- TestApplicationPreparedAction action2 = new TestApplicationPreparedAction();
- Registration registration = this.registy.register(Integer.class, this.counter::getAndIncrement);
- registration.onApplicationContextPrepared(action1::run);
- registration.onApplicationContextPrepared(action2::run);
- this.registy.applicationContextPrepared(this.context);
- assertThat(action1).wasCalledOnlyOnce().hasInstanceValue(0);
- assertThat(action2).wasCalledOnlyOnce().hasInstanceValue(0);
- }
-
- @Test
- void registrationGetReturnsInstance() {
- Registration registration = this.registy.register(Integer.class, this.counter::getAndIncrement);
- assertThat(registration.get()).isEqualTo(0);
- }
-
- @Test
- void registrationGetWhenCalledMultipleTimesReturnsSingleInstance() {
- Registration registration = this.registy.register(Integer.class, this.counter::getAndIncrement);
- assertThat(registration.get()).isEqualTo(0);
- assertThat(registration.get()).isEqualTo(0);
- assertThat(registration.get()).isEqualTo(0);
- }
-
- @Test
- void registrationOnApplicationContextPreparedAddsAction() {
- TestApplicationPreparedAction action = new TestApplicationPreparedAction();
- Registration registration = this.registy.register(Integer.class, this.counter::getAndIncrement);
- registration.onApplicationContextPrepared(action::run);
- this.registy.applicationContextPrepared(this.context);
- assertThat(action).wasCalledOnlyOnce().hasInstanceValue(0);
- }
-
- @Test
- void registrationOnApplicationContextPreparedWhenActionIsNullDoesNotAddAction() {
- Registration registration = this.registy.register(Integer.class, this.counter::getAndIncrement);
- registration.onApplicationContextPrepared(null);
- this.registy.applicationContextPrepared(this.context);
- }
-
- private static class TestApplicationPreparedAction implements AssertProvider {
-
- private Integer instance;
-
- private int called;
-
- void run(ConfigurableApplicationContext context, Integer instance) {
- this.instance = instance;
- this.called++;
- }
-
- @Override
- public ApplicationPreparedActionAssert assertThat() {
- return new ApplicationPreparedActionAssert(this);
- }
-
- }
-
- private static class ApplicationPreparedActionAssert
- extends AbstractAssert {
-
- ApplicationPreparedActionAssert(TestApplicationPreparedAction actual) {
- super(actual, ApplicationPreparedActionAssert.class);
- }
-
- ApplicationPreparedActionAssert hasInstanceValue(Integer expected) {
- assertThat(this.actual.instance).isEqualTo(expected);
- return this;
- }
-
- ApplicationPreparedActionAssert wasCalledOnlyOnce() {
- assertThat(this.actual.called).as("action calls").isEqualTo(1);
- return this;
- }
-
- ApplicationPreparedActionAssert wasNotCalled() {
- assertThat(this.actual.called).as("action calls").isEqualTo(0);
- return this;
- }
-
- }
-
-}
diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/EnvironmentPostProcessorApplicationListenerTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/EnvironmentPostProcessorApplicationListenerTests.java
index 8655c5ff89d..82147c55878 100644
--- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/EnvironmentPostProcessorApplicationListenerTests.java
+++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/EnvironmentPostProcessorApplicationListenerTests.java
@@ -20,6 +20,8 @@ import java.util.List;
import org.junit.jupiter.api.Test;
+import org.springframework.boot.BootstrapRegistry;
+import org.springframework.boot.DefaultBootstrapContext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.context.event.ApplicationFailedEvent;
@@ -45,23 +47,22 @@ class EnvironmentPostProcessorApplicationListenerTests {
private DeferredLogs deferredLogs = spy(new DeferredLogs());
- private DefaultBootstrapRegisty bootstrapRegistry = spy(new DefaultBootstrapRegisty());
+ private DefaultBootstrapContext bootstrapContext = spy(new DefaultBootstrapContext());
private EnvironmentPostProcessorApplicationListener listener = new EnvironmentPostProcessorApplicationListener(
- EnvironmentPostProcessorsFactory.of(TestEnvironmentPostProcessor.class), this.deferredLogs,
- this.bootstrapRegistry);
+ EnvironmentPostProcessorsFactory.of(TestEnvironmentPostProcessor.class), this.deferredLogs);
@Test
void createUsesSpringFactories() {
EnvironmentPostProcessorApplicationListener listener = new EnvironmentPostProcessorApplicationListener();
- assertThat(listener.getEnvironmentPostProcessors()).hasSizeGreaterThan(1);
+ assertThat(listener.getEnvironmentPostProcessors(this.bootstrapContext)).hasSizeGreaterThan(1);
}
@Test
void createWhenHasFactoryUsesFactory() {
EnvironmentPostProcessorApplicationListener listener = new EnvironmentPostProcessorApplicationListener(
EnvironmentPostProcessorsFactory.of(TestEnvironmentPostProcessor.class));
- List postProcessors = listener.getEnvironmentPostProcessors();
+ List postProcessors = listener.getEnvironmentPostProcessors(this.bootstrapContext);
assertThat(postProcessors).hasSize(1);
assertThat(postProcessors.get(0)).isInstanceOf(TestEnvironmentPostProcessor.class);
}
@@ -90,8 +91,8 @@ class EnvironmentPostProcessorApplicationListenerTests {
void onApplicationEventWhenApplicationEnvironmentPreparedEventCallsPostProcessors() {
SpringApplication application = mock(SpringApplication.class);
MockEnvironment environment = new MockEnvironment();
- ApplicationEnvironmentPreparedEvent event = new ApplicationEnvironmentPreparedEvent(application, new String[0],
- environment);
+ ApplicationEnvironmentPreparedEvent event = new ApplicationEnvironmentPreparedEvent(this.bootstrapContext,
+ application, new String[0], environment);
this.listener.onApplicationEvent(event);
assertThat(environment.getProperty("processed")).isEqualTo("true");
}
@@ -105,15 +106,6 @@ class EnvironmentPostProcessorApplicationListenerTests {
verify(this.deferredLogs).switchOverAll();
}
- @Test
- void onApplicationEventWhenApplicationPreparedEventTriggersRegistryActions() {
- SpringApplication application = mock(SpringApplication.class);
- ConfigurableApplicationContext context = mock(ConfigurableApplicationContext.class);
- ApplicationPreparedEvent event = new ApplicationPreparedEvent(application, new String[0], context);
- this.listener.onApplicationEvent(event);
- verify(this.bootstrapRegistry).applicationContextPrepared(context);
- }
-
@Test
void onApplicationEventWhenApplicationFailedEventSwitchesLogs() {
SpringApplication application = mock(SpringApplication.class);
diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/EnvironmentPostProcessorsFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/EnvironmentPostProcessorsFactoryTests.java
index 212ac2c1a94..fd5e6cd5b85 100644
--- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/EnvironmentPostProcessorsFactoryTests.java
+++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/EnvironmentPostProcessorsFactoryTests.java
@@ -21,6 +21,7 @@ import java.util.function.Supplier;
import org.junit.jupiter.api.Test;
+import org.springframework.boot.DefaultBootstrapContext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.logging.DeferredLogFactory;
import org.springframework.core.env.ConfigurableEnvironment;
@@ -36,13 +37,13 @@ class EnvironmentPostProcessorsFactoryTests {
private final DeferredLogFactory logFactory = Supplier::get;
- private final BootstrapRegistry bootstrapRegistry = new DefaultBootstrapRegisty();
+ private final DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
@Test
void fromSpringFactoriesReturnsFactory() {
EnvironmentPostProcessorsFactory factory = EnvironmentPostProcessorsFactory.fromSpringFactories(null);
List processors = factory.getEnvironmentPostProcessors(this.logFactory,
- this.bootstrapRegistry);
+ this.bootstrapContext);
assertThat(processors).hasSizeGreaterThan(1);
}
@@ -51,7 +52,7 @@ class EnvironmentPostProcessorsFactoryTests {
EnvironmentPostProcessorsFactory factory = EnvironmentPostProcessorsFactory
.of(TestEnvironmentPostProcessor.class);
List processors = factory.getEnvironmentPostProcessors(this.logFactory,
- this.bootstrapRegistry);
+ this.bootstrapContext);
assertThat(processors).hasSize(1);
assertThat(processors.get(0)).isInstanceOf(TestEnvironmentPostProcessor.class);
}
@@ -61,7 +62,7 @@ class EnvironmentPostProcessorsFactoryTests {
EnvironmentPostProcessorsFactory factory = EnvironmentPostProcessorsFactory
.of(TestEnvironmentPostProcessor.class.getName());
List processors = factory.getEnvironmentPostProcessors(this.logFactory,
- this.bootstrapRegistry);
+ this.bootstrapContext);
assertThat(processors).hasSize(1);
assertThat(processors.get(0)).isInstanceOf(TestEnvironmentPostProcessor.class);
}
diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/ReflectionEnvironmentPostProcessorsFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/ReflectionEnvironmentPostProcessorsFactoryTests.java
index 510c9be2cb9..568ddf59a1b 100644
--- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/ReflectionEnvironmentPostProcessorsFactoryTests.java
+++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/ReflectionEnvironmentPostProcessorsFactoryTests.java
@@ -24,6 +24,8 @@ import java.util.function.Supplier;
import org.apache.commons.logging.Log;
import org.junit.jupiter.api.Test;
+import org.springframework.boot.BootstrapRegistry;
+import org.springframework.boot.DefaultBootstrapContext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.logging.DeferredLogFactory;
import org.springframework.core.env.ConfigurableEnvironment;
@@ -40,7 +42,7 @@ class ReflectionEnvironmentPostProcessorsFactoryTests {
private final DeferredLogFactory logFactory = Supplier::get;
- private final BootstrapRegistry bootstrapRegistry = new DefaultBootstrapRegisty();
+ private final DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
@Test
void createWithClassesCreatesFactory() {
@@ -96,7 +98,7 @@ class ReflectionEnvironmentPostProcessorsFactoryTests {
ReflectionEnvironmentPostProcessorsFactory factory = new ReflectionEnvironmentPostProcessorsFactory(
BadEnvironmentPostProcessor.class.getName());
assertThatIllegalArgumentException()
- .isThrownBy(() -> factory.getEnvironmentPostProcessors(this.logFactory, this.bootstrapRegistry))
+ .isThrownBy(() -> factory.getEnvironmentPostProcessors(this.logFactory, this.bootstrapContext))
.withMessageContaining("Unable to instantiate");
}
@@ -115,7 +117,7 @@ class ReflectionEnvironmentPostProcessorsFactoryTests {
void createsSinglePostProcessor(Class> expectedType) {
List processors = this.factory.getEnvironmentPostProcessors(
ReflectionEnvironmentPostProcessorsFactoryTests.this.logFactory,
- ReflectionEnvironmentPostProcessorsFactoryTests.this.bootstrapRegistry);
+ ReflectionEnvironmentPostProcessorsFactoryTests.this.bootstrapContext);
assertThat(processors).hasSize(1);
assertThat(processors.get(0)).isInstanceOf(expectedType);
}
diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/build.gradle b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/build.gradle
new file mode 100644
index 00000000000..ad4cb96f738
--- /dev/null
+++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/build.gradle
@@ -0,0 +1,12 @@
+plugins {
+ id "java"
+ id "org.springframework.boot.conventions"
+}
+
+description = "Spring Boot Bootstrap Registry smoke test"
+
+dependencies {
+ implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter"))
+
+ testImplementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-test"))
+}
diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/app/MySubversionClient.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/app/MySubversionClient.java
new file mode 100644
index 00000000000..58ba2801ccf
--- /dev/null
+++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/app/MySubversionClient.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2012-2020 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 smoketest.bootstrapregistry.app;
+
+import smoketest.bootstrapregistry.external.svn.SubversionClient;
+import smoketest.bootstrapregistry.external.svn.SubversionServerCertificate;
+
+public class MySubversionClient extends SubversionClient {
+
+ public MySubversionClient(SubversionServerCertificate serverCertificate) {
+ super(serverCertificate);
+ }
+
+ @Override
+ public String load(String location) {
+ return "my-" + super.load(location);
+ }
+
+}
diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/app/Printer.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/app/Printer.java
new file mode 100644
index 00000000000..d9f7d1c7595
--- /dev/null
+++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/app/Printer.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2012-2020 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 smoketest.bootstrapregistry.app;
+
+import smoketest.bootstrapregistry.external.svn.SubversionClient;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+@Component
+public class Printer {
+
+ Printer(@Value("${svn}") String svn, SubversionClient subversionClient) {
+ System.out.println("--- svn " + svn);
+ System.out.println("--- client " + subversionClient.getClass().getName());
+ }
+
+}
diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/app/SampleBootstrapRegistryApplication.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/app/SampleBootstrapRegistryApplication.java
new file mode 100644
index 00000000000..0b41f26005e
--- /dev/null
+++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/app/SampleBootstrapRegistryApplication.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2012-2020 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 smoketest.bootstrapregistry.app;
+
+import smoketest.bootstrapregistry.external.svn.SubversionBootstrap;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class SampleBootstrapRegistryApplication {
+
+ public static void main(String[] args) {
+ // This example shows how a Bootstrapper can be used to register a custom
+ // SubversionClient that still has access to data provided in the
+ // application.properties file
+ SpringApplication application = new SpringApplication(SampleBootstrapRegistryApplication.class);
+ application.addBootstrapper(SubversionBootstrap.withCustomClient(MySubversionClient::new));
+ application.run(args);
+ }
+
+}
diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/external/svn/SubversionBootstrap.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/external/svn/SubversionBootstrap.java
new file mode 100644
index 00000000000..8617c36fe5b
--- /dev/null
+++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/external/svn/SubversionBootstrap.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2012-2020 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 smoketest.bootstrapregistry.external.svn;
+
+import java.util.function.Function;
+
+import org.springframework.boot.BootstrapContext;
+import org.springframework.boot.Bootstrapper;
+
+/**
+ * Allows the user to register a {@link Bootstrapper} with a custom
+ * {@link SubversionClient}.
+ *
+ * @author Phillip Webb
+ */
+public final class SubversionBootstrap {
+
+ private SubversionBootstrap() {
+ }
+
+ /**
+ * Return a {@link Bootstrapper} for the given client factory.
+ * @param clientFactory the client factory
+ * @return a {@link Bootstrapper} instance
+ */
+ public static Bootstrapper withCustomClient(Function clientFactory) {
+ return (registry) -> registry.register(SubversionClient.class,
+ (bootstrapContext) -> createSubversionClient(bootstrapContext, clientFactory));
+ }
+
+ private static SubversionClient createSubversionClient(BootstrapContext bootstrapContext,
+ Function clientFactory) {
+ return clientFactory.apply(bootstrapContext.get(SubversionServerCertificate.class));
+ }
+
+}
diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/external/svn/SubversionClient.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/external/svn/SubversionClient.java
new file mode 100644
index 00000000000..663d65d87ab
--- /dev/null
+++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/external/svn/SubversionClient.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2012-2020 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 smoketest.bootstrapregistry.external.svn;
+
+/**
+ * A client that can connect to a subversion server.
+ *
+ * @author Phillip Webb
+ */
+public class SubversionClient {
+
+ private SubversionServerCertificate serverCertificate;
+
+ public SubversionClient(SubversionServerCertificate serverCertificate) {
+ this.serverCertificate = serverCertificate;
+ }
+
+ public String load(String location) {
+ return "data from svn / " + location + "[" + this.serverCertificate + "]";
+ }
+
+}
diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/external/svn/SubversionConfigDataLoader.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/external/svn/SubversionConfigDataLoader.java
new file mode 100644
index 00000000000..6ef1bca6235
--- /dev/null
+++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/external/svn/SubversionConfigDataLoader.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2012-2020 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 smoketest.bootstrapregistry.external.svn;
+
+import java.io.IOException;
+import java.util.Collections;
+
+import org.springframework.boot.BootstrapContext;
+import org.springframework.boot.BootstrapContextClosedEvent;
+import org.springframework.boot.BootstrapRegistry;
+import org.springframework.boot.BootstrapRegistry.InstanceSupplier;
+import org.springframework.boot.context.config.ConfigData;
+import org.springframework.boot.context.config.ConfigDataLoader;
+import org.springframework.boot.context.config.ConfigDataLoaderContext;
+import org.springframework.boot.context.config.ConfigDataLocationNotFoundException;
+import org.springframework.context.ApplicationListener;
+import org.springframework.core.env.MapPropertySource;
+import org.springframework.core.env.PropertySource;
+
+/**
+ * {@link ConfigDataLoader} for subversion.
+ *
+ * @author Phillip Webb
+ */
+class SubversionConfigDataLoader implements ConfigDataLoader {
+
+ private static final ApplicationListener closeListener = SubversionConfigDataLoader::onBootstrapContextClosed;
+
+ SubversionConfigDataLoader(BootstrapRegistry bootstrapRegistry) {
+ bootstrapRegistry.registerIfAbsent(SubversionClient.class, this::createSubversionClient);
+ bootstrapRegistry.addCloseListener(closeListener);
+ }
+
+ private SubversionClient createSubversionClient(BootstrapContext bootstrapContext) {
+ return new SubversionClient(bootstrapContext.get(SubversionServerCertificate.class));
+ }
+
+ @Override
+ public ConfigData load(ConfigDataLoaderContext context, SubversionConfigDataLocation location)
+ throws IOException, ConfigDataLocationNotFoundException {
+ context.getBootstrapContext().registerIfAbsent(SubversionServerCertificate.class,
+ InstanceSupplier.of(location.getServerCertificate()));
+ SubversionClient client = context.getBootstrapContext().get(SubversionClient.class);
+ String loaded = client.load(location.getLocation());
+ PropertySource> propertySource = new MapPropertySource("svn", Collections.singletonMap("svn", loaded));
+ return new ConfigData(Collections.singleton(propertySource));
+ }
+
+ private static void onBootstrapContextClosed(BootstrapContextClosedEvent event) {
+ event.getApplicationContext().getBeanFactory().registerSingleton("subversionClient",
+ event.getBootstrapContext().get(SubversionClient.class));
+ }
+
+}
diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/external/svn/SubversionConfigDataLocation.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/external/svn/SubversionConfigDataLocation.java
new file mode 100644
index 00000000000..d812af0ac3a
--- /dev/null
+++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/external/svn/SubversionConfigDataLocation.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2012-2020 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 smoketest.bootstrapregistry.external.svn;
+
+import org.springframework.boot.context.config.ConfigDataLocation;
+
+/**
+ * A subversion {@link ConfigDataLocation}.
+ *
+ * @author Phillip Webb
+ */
+class SubversionConfigDataLocation extends ConfigDataLocation {
+
+ private final String location;
+
+ private final SubversionServerCertificate serverCertificate;
+
+ SubversionConfigDataLocation(String location, String serverCertificate) {
+ this.location = location;
+ this.serverCertificate = SubversionServerCertificate.of(serverCertificate);
+ }
+
+ String getLocation() {
+ return this.location;
+ }
+
+ SubversionServerCertificate getServerCertificate() {
+ return this.serverCertificate;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ SubversionConfigDataLocation other = (SubversionConfigDataLocation) obj;
+ return this.location.equals(other.location);
+ }
+
+ @Override
+ public int hashCode() {
+ return this.location.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return this.location;
+ }
+
+}
diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/external/svn/SubversionConfigDataLocationResolver.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/external/svn/SubversionConfigDataLocationResolver.java
new file mode 100644
index 00000000000..74ea940a352
--- /dev/null
+++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/external/svn/SubversionConfigDataLocationResolver.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2012-2020 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 smoketest.bootstrapregistry.external.svn;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.springframework.boot.context.config.ConfigDataLocationNotFoundException;
+import org.springframework.boot.context.config.ConfigDataLocationResolver;
+import org.springframework.boot.context.config.ConfigDataLocationResolverContext;
+
+/**
+ * {@link ConfigDataLocationResolver} for subversion.
+ *
+ * @author Phillip Webb
+ */
+class SubversionConfigDataLocationResolver implements ConfigDataLocationResolver {
+
+ private static final String PREFIX = "svn:";
+
+ @Override
+ public boolean isResolvable(ConfigDataLocationResolverContext context, String location) {
+ return location.startsWith(PREFIX);
+ }
+
+ @Override
+ public List resolve(ConfigDataLocationResolverContext context, String location,
+ boolean optional) throws ConfigDataLocationNotFoundException {
+ String serverCertificate = context.getBinder().bind("spring.svn.server.certificate", String.class).orElse(null);
+ return Collections.singletonList(
+ new SubversionConfigDataLocation(location.substring(PREFIX.length()), serverCertificate));
+ }
+
+}
diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/external/svn/SubversionServerCertificate.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/external/svn/SubversionServerCertificate.java
new file mode 100644
index 00000000000..ffb9ff3d146
--- /dev/null
+++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/external/svn/SubversionServerCertificate.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2012-2020 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 smoketest.bootstrapregistry.external.svn;
+
+import org.springframework.util.StringUtils;
+
+/**
+ * A certificate that can be used to provide a secure connection to the subversion server.
+ *
+ * @author Phillip Webb
+ */
+public class SubversionServerCertificate {
+
+ private final String data;
+
+ SubversionServerCertificate(String data) {
+ this.data = data;
+ }
+
+ @Override
+ public String toString() {
+ return this.data;
+ }
+
+ public static SubversionServerCertificate of(String data) {
+ return StringUtils.hasText(data) ? new SubversionServerCertificate(data) : null;
+ }
+
+}
diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/external/svn/package-info.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/external/svn/package-info.java
new file mode 100644
index 00000000000..d6c317d7c80
--- /dev/null
+++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/external/svn/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2012-2020 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.
+ */
+
+/**
+ * An example of a hypothetical library that supports subversion.
+ */
+package smoketest.bootstrapregistry.external.svn;
diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/resources/META-INF/spring.factories b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/resources/META-INF/spring.factories
new file mode 100644
index 00000000000..2bc21d1989e
--- /dev/null
+++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,5 @@
+org.springframework.boot.context.config.ConfigDataLocationResolver=\
+smoketest.bootstrapregistry.external.svn.SubversionConfigDataLocationResolver
+
+org.springframework.boot.context.config.ConfigDataLoader=\
+smoketest.bootstrapregistry.external.svn.SubversionConfigDataLoader
diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/resources/application.properties b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/resources/application.properties
new file mode 100644
index 00000000000..d7f98258df5
--- /dev/null
+++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/resources/application.properties
@@ -0,0 +1,2 @@
+spring.svn.server.certificate=secret
+spring.config.import=svn:example.com
diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/test/java/smoketest/bootstrapregistry/app/SampleBootstrapRegistryApplicationTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/test/java/smoketest/bootstrapregistry/app/SampleBootstrapRegistryApplicationTests.java
new file mode 100644
index 00000000000..3dd99987ce4
--- /dev/null
+++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/test/java/smoketest/bootstrapregistry/app/SampleBootstrapRegistryApplicationTests.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2012-2020 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 smoketest.bootstrapregistry.app;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import org.springframework.boot.test.system.CapturedOutput;
+import org.springframework.boot.test.system.OutputCaptureExtension;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests for {@link SampleBootstrapRegistryApplication}.
+ *
+ * @author Phillip Webb
+ */
+@ExtendWith(OutputCaptureExtension.class)
+class SampleBootstrapRegistryApplicationTests {
+
+ @Test
+ void testBootrapper(CapturedOutput output) {
+ SampleBootstrapRegistryApplication.main(new String[0]);
+ assertThat(output).contains("svn my-data from svn / example.com[secret]")
+ .contains("client smoketest.bootstrapregistry.app.MySubversionClient");
+ }
+
+}