Skip to content

Commit 1ae1436

Browse files
committed
Refactor BootstrapRegistry support
Refactor `BootstrapRegistry` support following initial prototype work with the Spring Cloud team. This update splits the `BootstrapRegistry` API into `BootstrapRegistry`, `BootstrapContext` and `ConfigurableBootstrapContext` interfaces and moves it to the same package as `SpringApplication`. A new `Bootstrapper` interface has been introduced that can be added to the `SpringApplication` to customize the `BootstrapRegistry` before it's used. Closes gh-23326
1 parent 27095d9 commit 1ae1436

File tree

64 files changed

+1634
-673
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+1634
-673
lines changed

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfigurationTests.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737

3838
import org.springframework.beans.factory.BeanCreationException;
3939
import org.springframework.beans.factory.config.BeanDefinition;
40+
import org.springframework.boot.DefaultBootstrapContext;
4041
import org.springframework.boot.SpringApplication;
4142
import org.springframework.boot.autoconfigure.AutoConfigurations;
4243
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
@@ -78,8 +79,8 @@ class LiquibaseAutoConfigurationTests {
7879

7980
@BeforeEach
8081
void init() {
81-
new LiquibaseServiceLocatorApplicationListener()
82-
.onApplicationEvent(new ApplicationStartingEvent(new SpringApplication(Object.class), new String[0]));
82+
new LiquibaseServiceLocatorApplicationListener().onApplicationEvent(new ApplicationStartingEvent(
83+
new DefaultBootstrapContext(), new SpringApplication(Object.class), new String[0]));
8384
}
8485

8586
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()

spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/RestartApplicationListenerTests.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@
2323
import org.junit.jupiter.api.Test;
2424
import org.junit.jupiter.api.extension.ExtendWith;
2525

26+
import org.springframework.boot.DefaultBootstrapContext;
2627
import org.springframework.boot.SpringApplication;
2728
import org.springframework.boot.context.event.ApplicationFailedEvent;
2829
import org.springframework.boot.context.event.ApplicationPreparedEvent;
@@ -90,9 +91,10 @@ void disableWithSystemProperty(CapturedOutput output) {
9091
private void testInitialize(boolean failed) {
9192
Restarter.clearInstance();
9293
RestartApplicationListener listener = new RestartApplicationListener();
94+
DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
9395
SpringApplication application = new SpringApplication();
9496
ConfigurableApplicationContext context = mock(ConfigurableApplicationContext.class);
95-
listener.onApplicationEvent(new ApplicationStartingEvent(application, ARGS));
97+
listener.onApplicationEvent(new ApplicationStartingEvent(bootstrapContext, application, ARGS));
9698
assertThat(Restarter.getInstance()).isNotEqualTo(nullValue());
9799
assertThat(Restarter.getInstance().isFinished()).isFalse();
98100
listener.onApplicationEvent(new ApplicationPreparedEvent(application, ARGS, context));

spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/ConfigDataApplicationContextInitializer.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@
1616

1717
package org.springframework.boot.test.context;
1818

19+
import org.springframework.boot.DefaultBootstrapContext;
1920
import org.springframework.boot.DefaultPropertiesPropertySource;
2021
import org.springframework.boot.context.config.ConfigData;
2122
import org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor;
22-
import org.springframework.boot.env.DefaultBootstrapRegisty;
2323
import org.springframework.boot.env.RandomValuePropertySource;
2424
import org.springframework.context.ApplicationContextInitializer;
2525
import org.springframework.context.ConfigurableApplicationContext;
@@ -42,9 +42,9 @@ public class ConfigDataApplicationContextInitializer
4242
public void initialize(ConfigurableApplicationContext applicationContext) {
4343
ConfigurableEnvironment environment = applicationContext.getEnvironment();
4444
RandomValuePropertySource.addToEnvironment(environment);
45-
DefaultBootstrapRegisty bootstrapRegistry = new DefaultBootstrapRegisty();
46-
ConfigDataEnvironmentPostProcessor.applyTo(environment, applicationContext, bootstrapRegistry);
47-
bootstrapRegistry.applicationContextPrepared(applicationContext);
45+
DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
46+
ConfigDataEnvironmentPostProcessor.applyTo(environment, applicationContext, bootstrapContext);
47+
bootstrapContext.close(applicationContext);
4848
DefaultPropertiesPropertySource.moveToEnd(environment);
4949
}
5050

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2012-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot;
18+
19+
import org.springframework.context.ApplicationContext;
20+
import org.springframework.core.env.Environment;
21+
22+
/**
23+
* A simple bootstrap context that is available during startup and {@link Environment}
24+
* post-processing up to the point that the {@link ApplicationContext} is prepared.
25+
* <p>
26+
* Provides lazy access to singletons that may be expensive to create, or need to be
27+
* shared before the {@link ApplicationContext} is available.
28+
*
29+
* @author Phillip Webb
30+
* @since 2.4.0
31+
*/
32+
public interface BootstrapContext {
33+
34+
/**
35+
* Return an instance from the context, creating it if it hasn't been accessed
36+
* previously.
37+
* @param <T> the instance type
38+
* @param type the instance type
39+
* @return the instance managed by the context
40+
* @throws IllegalStateException if the type has not been registered
41+
*/
42+
<T> T get(Class<T> type) throws IllegalStateException;
43+
44+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright 2012-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot;
18+
19+
import org.springframework.context.ApplicationEvent;
20+
import org.springframework.context.ConfigurableApplicationContext;
21+
22+
/**
23+
* {@link ApplicationEvent} published by a {@link BootstrapContext} when it's closed.
24+
*
25+
* @author Phillip Webb
26+
* @since 2.4.0
27+
* @see BootstrapRegistry#addCloseListener(org.springframework.context.ApplicationListener)
28+
*/
29+
public class BootstrapContextClosedEvent extends ApplicationEvent {
30+
31+
private final ConfigurableApplicationContext applicationContext;
32+
33+
BootstrapContextClosedEvent(BootstrapContext source, ConfigurableApplicationContext applicationContext) {
34+
super(source);
35+
this.applicationContext = applicationContext;
36+
}
37+
38+
/**
39+
* Return the {@link BootstrapContext} that was closed.
40+
* @return the bootstrap context
41+
*/
42+
public BootstrapContext getBootstrapContext() {
43+
return (BootstrapContext) this.source;
44+
}
45+
46+
/**
47+
* Return the prepared application context.
48+
* @return the application context
49+
*/
50+
public ConfigurableApplicationContext getApplicationContext() {
51+
return this.applicationContext;
52+
}
53+
54+
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/*
2+
* Copyright 2012-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot;
18+
19+
import java.util.function.Supplier;
20+
21+
import io.undertow.servlet.api.InstanceFactory;
22+
23+
import org.springframework.context.ApplicationContext;
24+
import org.springframework.context.ApplicationListener;
25+
import org.springframework.core.env.Environment;
26+
27+
/**
28+
* A simple object registry that is available during startup and {@link Environment}
29+
* post-processing up to the point that the {@link ApplicationContext} is prepared.
30+
* <p>
31+
* Can be used to register instances that may be expensive to create, or need to be shared
32+
* before the {@link ApplicationContext} is available.
33+
* <p>
34+
* The registry uses {@link Class} as a key, meaning that only a single instance of a
35+
* given type can be stored.
36+
* <p>
37+
* The {@link #addCloseListener(ApplicationListener)} method can be used to add a listener
38+
* that can perform actions when {@link BootstrapContext} has been closed and the
39+
* {@link ApplicationContext} is fully prepared. For example, an instance may choose to
40+
* register itself as a regular Spring bean so that it is available for the application to
41+
* use.
42+
*
43+
* @author Phillip Webb
44+
* @since 2.4.0
45+
* @see BootstrapContext
46+
* @see ConfigurableBootstrapContext
47+
*/
48+
public interface BootstrapRegistry {
49+
50+
/**
51+
* Register a specific type with the registry. If the specified type has already been
52+
* registered, but not get obtained, it will be replaced.
53+
* @param <T> the instance type
54+
* @param type the instance type
55+
* @param instanceSupplier the instance supplier
56+
*/
57+
<T> void register(Class<T> type, InstanceSupplier<T> instanceSupplier);
58+
59+
/**
60+
* Register a specific type with the registry if one is not already present.
61+
* @param <T> the instance type
62+
* @param type the instance type
63+
* @param instanceSupplier the instance supplier
64+
*/
65+
<T> void registerIfAbsent(Class<T> type, InstanceSupplier<T> instanceSupplier);
66+
67+
/**
68+
* Return if a registration exists for the given type.
69+
* @param <T> the instance type
70+
* @param type the instance type
71+
* @return {@code true} if the type has already been registered
72+
*/
73+
<T> boolean isRegistered(Class<T> type);
74+
75+
/**
76+
* Return any existing {@link InstanceFactory} for the given type.
77+
* @param <T> the instance type
78+
* @param type the instance type
79+
* @return the registered {@link InstanceSupplier} or {@code null}
80+
*/
81+
<T> InstanceSupplier<T> getRegisteredInstanceSupplier(Class<T> type);
82+
83+
/**
84+
* Add an {@link ApplicationListener} that will be called with a
85+
* {@link BootstrapContextClosedEvent} when the {@link BootstrapContext} is closed and
86+
* the {@link ApplicationContext} has been prepared.
87+
* @param listener the listener to add
88+
*/
89+
void addCloseListener(ApplicationListener<BootstrapContextClosedEvent> listener);
90+
91+
/**
92+
* Supplier used to provide the actual instance the first time it is accessed.
93+
*
94+
* @param <T> the instance type
95+
*/
96+
public interface InstanceSupplier<T> {
97+
98+
/**
99+
* Factory method used to create the instance when needed.
100+
* @param context the {@link BootstrapContext} which may be used to obtain other
101+
* bootstrap instances.
102+
* @return the instance
103+
*/
104+
T get(BootstrapContext context);
105+
106+
/**
107+
* Factory method that can be used to create a {@link InstanceFactory} for a given
108+
* instance.
109+
* @param <T> the instance type
110+
* @param instance the instance
111+
* @return a new {@link InstanceFactory}
112+
*/
113+
static <T> InstanceSupplier<T> of(T instance) {
114+
return (registry) -> instance;
115+
}
116+
117+
/**
118+
* Factory method that can be used to create a {@link InstanceFactory} from a
119+
* {@link Supplier}.
120+
* @param <T> the instance type
121+
* @param supplier the supplier that will provide the instance
122+
* @return a new {@link InstanceFactory}
123+
*/
124+
static <T> InstanceSupplier<T> from(Supplier<T> supplier) {
125+
return (registry) -> (supplier != null) ? supplier.get() : null;
126+
}
127+
128+
}
129+
130+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2012-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot;
18+
19+
/**
20+
* Callback interface that can be used to initialize a {@link BootstrapRegistry} before it
21+
* is used.
22+
*
23+
* @author Phillip Webb
24+
* @since 2.4.0
25+
* @see SpringApplication#addBootstrapper(Bootstrapper)
26+
* @see BootstrapRegistry
27+
*/
28+
public interface Bootstrapper {
29+
30+
/**
31+
* Initialize the given {@link BootstrapRegistry} with any required registrations.
32+
* @param registry the registry to initialize
33+
*/
34+
void intitialize(BootstrapRegistry registry);
35+
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright 2012-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot;
18+
19+
/**
20+
* A {@link BootstrapContext} that also provides configuration methods via the
21+
* {@link BootstrapRegistry} interface.
22+
*
23+
* @author Phillip Webb
24+
* @since 2.4.0
25+
* @see BootstrapRegistry
26+
* @see BootstrapContext
27+
* @see DefaultBootstrapContext
28+
*/
29+
public interface ConfigurableBootstrapContext extends BootstrapRegistry, BootstrapContext {
30+
31+
}

0 commit comments

Comments
 (0)