Skip to content

Commit d123c92

Browse files
committed
Add BootstrapRegisty support for config data
Expose the `BootstrapRegisty` to both `ConfigDataLocationResolver` and `ConfigDataLoader` implementations. The registry is exposed via the context interfaces and may be used to reuse instances that are expensive to create. It may also be used to ultimately register beans with the `ApplicationContext`. Closes gh-22956
1 parent 2260657 commit d123c92

26 files changed

+491
-113
lines changed

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import org.springframework.boot.context.config.ConfigData;
2020
import org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor;
21+
import org.springframework.boot.env.DefaultBootstrapRegisty;
2122
import org.springframework.boot.env.DefaultPropertiesPropertySource;
2223
import org.springframework.boot.env.RandomValuePropertySource;
2324
import org.springframework.context.ApplicationContextInitializer;
@@ -41,7 +42,9 @@ public class ConfigDataApplicationContextInitializer
4142
public void initialize(ConfigurableApplicationContext applicationContext) {
4243
ConfigurableEnvironment environment = applicationContext.getEnvironment();
4344
RandomValuePropertySource.addToEnvironment(environment);
44-
ConfigDataEnvironmentPostProcessor.applyTo(environment, applicationContext);
45+
DefaultBootstrapRegisty bootstrapRegistry = new DefaultBootstrapRegisty();
46+
ConfigDataEnvironmentPostProcessor.applyTo(environment, applicationContext, bootstrapRegistry);
47+
bootstrapRegistry.applicationContextPrepared(applicationContext);
4548
DefaultPropertiesPropertySource.moveToEnd(environment);
4649
}
4750

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.springframework.boot.context.config.ConfigDataEnvironmentContributors.BinderOption;
2626
import org.springframework.boot.context.properties.bind.BindException;
2727
import org.springframework.boot.context.properties.bind.Binder;
28+
import org.springframework.boot.env.BootstrapRegistry;
2829
import org.springframework.boot.env.DefaultPropertiesPropertySource;
2930
import org.springframework.boot.logging.DeferredLogFactory;
3031
import org.springframework.core.env.ConfigurableEnvironment;
@@ -77,6 +78,8 @@ class ConfigDataEnvironment {
7778

7879
private final Log logger;
7980

81+
private final BootstrapRegistry bootstrapRegistry;
82+
8083
private final ConfigurableEnvironment environment;
8184

8285
private final ConfigDataLocationResolvers resolvers;
@@ -90,16 +93,18 @@ class ConfigDataEnvironment {
9093
/**
9194
* Create a new {@link ConfigDataEnvironment} instance.
9295
* @param logFactory the deferred log factory
96+
* @param bootstrapRegistry the bootstrap registry
9397
* @param environment the Spring {@link Environment}.
9498
* @param resourceLoader {@link ResourceLoader} to load resource locations
9599
* @param additionalProfiles any additional profiles to activate
96100
*/
97-
ConfigDataEnvironment(DeferredLogFactory logFactory, ConfigurableEnvironment environment,
98-
ResourceLoader resourceLoader, Collection<String> additionalProfiles) {
101+
ConfigDataEnvironment(DeferredLogFactory logFactory, BootstrapRegistry bootstrapRegistry,
102+
ConfigurableEnvironment environment, ResourceLoader resourceLoader, Collection<String> additionalProfiles) {
99103
Binder binder = Binder.get(environment);
100104
UseLegacyConfigProcessingException.throwIfRequested(binder);
101105
this.logFactory = logFactory;
102106
this.logger = logFactory.getLog(getClass());
107+
this.bootstrapRegistry = bootstrapRegistry;
103108
this.environment = environment;
104109
this.resolvers = createConfigDataLocationResolvers(logFactory, binder, resourceLoader);
105110
this.additionalProfiles = additionalProfiles;
@@ -132,7 +137,7 @@ private ConfigDataEnvironmentContributors createContributors(Binder binder) {
132137
this.logger.trace("Creating wrapped config data contributor for default property source");
133138
contributors.add(ConfigDataEnvironmentContributor.ofExisting(defaultPropertySource));
134139
}
135-
return new ConfigDataEnvironmentContributors(this.logFactory, contributors);
140+
return new ConfigDataEnvironmentContributors(this.logFactory, this.bootstrapRegistry, contributors);
136141
}
137142

138143
ConfigDataEnvironmentContributors getContributors() {

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributors.java

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.springframework.boot.context.properties.bind.PlaceholdersResolver;
3737
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
3838
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
39+
import org.springframework.boot.env.BootstrapRegistry;
3940
import org.springframework.boot.logging.DeferredLogFactory;
4041
import org.springframework.core.log.LogMessage;
4142
import org.springframework.util.ObjectUtils;
@@ -53,19 +54,25 @@ class ConfigDataEnvironmentContributors implements Iterable<ConfigDataEnvironmen
5354

5455
private final ConfigDataEnvironmentContributor root;
5556

57+
private final BootstrapRegistry bootstrapRegistry;
58+
5659
/**
5760
* Create a new {@link ConfigDataEnvironmentContributors} instance.
5861
* @param logFactory the log factory
62+
* @param bootstrapRegistry the bootstrap registry
5963
* @param contributors the initial set of contributors
6064
*/
61-
ConfigDataEnvironmentContributors(DeferredLogFactory logFactory,
65+
ConfigDataEnvironmentContributors(DeferredLogFactory logFactory, BootstrapRegistry bootstrapRegistry,
6266
List<ConfigDataEnvironmentContributor> contributors) {
6367
this.logger = logFactory.getLog(getClass());
68+
this.bootstrapRegistry = bootstrapRegistry;
6469
this.root = ConfigDataEnvironmentContributor.of(contributors);
6570
}
6671

67-
private ConfigDataEnvironmentContributors(Log logger, ConfigDataEnvironmentContributor root) {
72+
private ConfigDataEnvironmentContributors(Log logger, BootstrapRegistry bootstrapRegistry,
73+
ConfigDataEnvironmentContributor root) {
6874
this.logger = logger;
75+
this.bootstrapRegistry = bootstrapRegistry;
6976
this.root = root;
7077
}
7178

@@ -91,22 +98,27 @@ ConfigDataEnvironmentContributors withProcessedImports(ConfigDataImporter import
9198
this.logger.trace(LogMessage.format("Processed imports for of %d contributors", processedCount));
9299
return result;
93100
}
94-
ConfigDataLocationResolverContext locationResolverContext = new ContributorLocationResolverContext(result,
95-
unprocessed, activationContext);
101+
ConfigDataLocationResolverContext locationResolverContext = new ContributorConfigDataLocationResolverContext(
102+
result, unprocessed, activationContext);
103+
ConfigDataLoaderContext loaderContext = new ContributorDataLoaderContext(this);
96104
List<String> imports = unprocessed.getImports();
97105
this.logger.trace(LogMessage.format("Processing imports %s", imports));
98106
Map<ConfigDataLocation, ConfigData> imported = importer.resolveAndLoad(activationContext,
99-
locationResolverContext, imports);
107+
locationResolverContext, loaderContext, imports);
100108
this.logger.trace(LogMessage.of(() -> imported.isEmpty() ? "Nothing imported" : "Imported "
101109
+ imported.size() + " location " + ((imported.size() != 1) ? "s" : "") + imported.keySet()));
102110
ConfigDataEnvironmentContributor processed = unprocessed.withChildren(importPhase,
103111
asContributors(activationContext, imported));
104-
result = new ConfigDataEnvironmentContributors(this.logger,
112+
result = new ConfigDataEnvironmentContributors(this.logger, this.bootstrapRegistry,
105113
result.getRoot().withReplacement(unprocessed, processed));
106114
processedCount++;
107115
}
108116
}
109117

118+
protected final BootstrapRegistry getBootstrapRegistry() {
119+
return this.bootstrapRegistry;
120+
}
121+
110122
private ConfigDataEnvironmentContributor getFirstUnprocessed(ConfigDataEnvironmentContributors contributors,
111123
ConfigDataActivationContext activationContext, ImportPhase importPhase) {
112124
for (ConfigDataEnvironmentContributor contributor : contributors.getRoot()) {
@@ -177,10 +189,27 @@ public Iterator<ConfigDataEnvironmentContributor> iterator() {
177189
}
178190

179191
/**
180-
* {@link ConfigDataLocationResolverContext} backed by a
181-
* {@link ConfigDataEnvironmentContributor}.
192+
* {@link ConfigDataLocationResolverContext} for a contributor.
182193
*/
183-
private static class ContributorLocationResolverContext implements ConfigDataLocationResolverContext {
194+
private static class ContributorDataLoaderContext implements ConfigDataLoaderContext {
195+
196+
private final ConfigDataEnvironmentContributors contributors;
197+
198+
ContributorDataLoaderContext(ConfigDataEnvironmentContributors contributors) {
199+
this.contributors = contributors;
200+
}
201+
202+
@Override
203+
public BootstrapRegistry getBootstrapRegistry() {
204+
return this.contributors.getBootstrapRegistry();
205+
}
206+
207+
}
208+
209+
/**
210+
* {@link ConfigDataLocationResolverContext} for a contributor.
211+
*/
212+
private static class ContributorConfigDataLocationResolverContext implements ConfigDataLocationResolverContext {
184213

185214
private final ConfigDataEnvironmentContributors contributors;
186215

@@ -190,7 +219,7 @@ private static class ContributorLocationResolverContext implements ConfigDataLoc
190219

191220
private volatile Binder binder;
192221

193-
ContributorLocationResolverContext(ConfigDataEnvironmentContributors contributors,
222+
ContributorConfigDataLocationResolverContext(ConfigDataEnvironmentContributors contributors,
194223
ConfigDataEnvironmentContributor contributor, ConfigDataActivationContext activationContext) {
195224
this.contributors = contributors;
196225
this.contributor = contributor;
@@ -212,6 +241,11 @@ public ConfigDataLocation getParent() {
212241
return this.contributor.getLocation();
213242
}
214243

244+
@Override
245+
public BootstrapRegistry getBootstrapRegistry() {
246+
return this.contributors.getBootstrapRegistry();
247+
}
248+
215249
}
216250

217251
private class InactiveSourceChecker implements BindHandler {

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessor.java

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import org.apache.commons.logging.Log;
2525

2626
import org.springframework.boot.SpringApplication;
27+
import org.springframework.boot.env.BootstrapRegistry;
28+
import org.springframework.boot.env.DefaultBootstrapRegisty;
2729
import org.springframework.boot.env.EnvironmentPostProcessor;
2830
import org.springframework.boot.logging.DeferredLogFactory;
2931
import org.springframework.core.Ordered;
@@ -52,9 +54,12 @@ public class ConfigDataEnvironmentPostProcessor implements EnvironmentPostProces
5254

5355
private final Log logger;
5456

55-
public ConfigDataEnvironmentPostProcessor(DeferredLogFactory logFactory) {
57+
private final BootstrapRegistry bootstrapRegistry;
58+
59+
public ConfigDataEnvironmentPostProcessor(DeferredLogFactory logFactory, BootstrapRegistry bootstrapRegistry) {
5660
this.logFactory = logFactory;
5761
this.logger = logFactory.getLog(getClass());
62+
this.bootstrapRegistry = bootstrapRegistry;
5863
}
5964

6065
@Override
@@ -83,7 +88,8 @@ void postProcessEnvironment(ConfigurableEnvironment environment, ResourceLoader
8388

8489
ConfigDataEnvironment getConfigDataEnvironment(ConfigurableEnvironment environment, ResourceLoader resourceLoader,
8590
Collection<String> additionalProfiles) {
86-
return new ConfigDataEnvironment(this.logFactory, environment, resourceLoader, additionalProfiles);
91+
return new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry, environment, resourceLoader,
92+
additionalProfiles);
8793
}
8894

8995
private void postProcessUsingLegacyApplicationListener(ConfigurableEnvironment environment,
@@ -103,7 +109,7 @@ LegacyConfigFileApplicationListener getLegacyListener() {
103109
* @param environment the environment to apply {@link ConfigData} to
104110
*/
105111
public static void applyTo(ConfigurableEnvironment environment) {
106-
applyTo(environment, null, Collections.emptyList());
112+
applyTo(environment, null, null, Collections.emptyList());
107113
}
108114

109115
/**
@@ -112,11 +118,13 @@ public static void applyTo(ConfigurableEnvironment environment) {
112118
* directly and not necessarily as part of a {@link SpringApplication}.
113119
* @param environment the environment to apply {@link ConfigData} to
114120
* @param resourceLoader the resource loader to use
121+
* @param bootstrapRegistry the bootstrap registry to use or {@code null} to use a
122+
* throw-away registry
115123
* @param additionalProfiles any additional profiles that should be applied
116124
*/
117125
public static void applyTo(ConfigurableEnvironment environment, ResourceLoader resourceLoader,
118-
String... additionalProfiles) {
119-
applyTo(environment, resourceLoader, Arrays.asList(additionalProfiles));
126+
BootstrapRegistry bootstrapRegistry, String... additionalProfiles) {
127+
applyTo(environment, resourceLoader, bootstrapRegistry, Arrays.asList(additionalProfiles));
120128
}
121129

122130
/**
@@ -125,13 +133,17 @@ public static void applyTo(ConfigurableEnvironment environment, ResourceLoader r
125133
* directly and not necessarily as part of a {@link SpringApplication}.
126134
* @param environment the environment to apply {@link ConfigData} to
127135
* @param resourceLoader the resource loader to use
136+
* @param bootstrapRegistry the bootstrap registry to use or {@code null} to use a
137+
* throw-away registry
128138
* @param additionalProfiles any additional profiles that should be applied
129139
*/
130140
public static void applyTo(ConfigurableEnvironment environment, ResourceLoader resourceLoader,
131-
Collection<String> additionalProfiles) {
132-
new ConfigDataEnvironmentPostProcessor(Supplier::get).postProcessEnvironment(environment, resourceLoader,
133-
additionalProfiles);
134-
141+
BootstrapRegistry bootstrapRegistry, Collection<String> additionalProfiles) {
142+
DeferredLogFactory logFactory = Supplier::get;
143+
bootstrapRegistry = (bootstrapRegistry != null) ? bootstrapRegistry : new DefaultBootstrapRegisty();
144+
ConfigDataEnvironmentPostProcessor postProcessor = new ConfigDataEnvironmentPostProcessor(logFactory,
145+
bootstrapRegistry);
146+
postProcessor.postProcessEnvironment(environment, resourceLoader, additionalProfiles);
135147
}
136148

137149
@SuppressWarnings("deprecation")

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataImporter.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,26 +55,29 @@ class ConfigDataImporter {
5555
* previously loaded.
5656
* @param activationContext the activation context
5757
* @param locationResolverContext the location resolver context
58+
* @param loaderContext the loader context
5859
* @param locations the locations to resolve
5960
* @return a map of the loaded locations and data
6061
*/
6162
Map<ConfigDataLocation, ConfigData> resolveAndLoad(ConfigDataActivationContext activationContext,
62-
ConfigDataLocationResolverContext locationResolverContext, List<String> locations) {
63+
ConfigDataLocationResolverContext locationResolverContext, ConfigDataLoaderContext loaderContext,
64+
List<String> locations) {
6365
try {
6466
Profiles profiles = (activationContext != null) ? activationContext.getProfiles() : null;
65-
return load(this.resolvers.resolveAll(locationResolverContext, locations, profiles));
67+
return load(loaderContext, this.resolvers.resolveAll(locationResolverContext, locations, profiles));
6668
}
6769
catch (IOException ex) {
6870
throw new IllegalStateException("IO error on loading imports from " + locations, ex);
6971
}
7072
}
7173

72-
private Map<ConfigDataLocation, ConfigData> load(List<ConfigDataLocation> locations) throws IOException {
74+
private Map<ConfigDataLocation, ConfigData> load(ConfigDataLoaderContext loaderContext,
75+
List<ConfigDataLocation> locations) throws IOException {
7376
Map<ConfigDataLocation, ConfigData> result = new LinkedHashMap<>();
7477
for (int i = locations.size() - 1; i >= 0; i--) {
7578
ConfigDataLocation location = locations.get(i);
7679
if (this.loadedLocations.add(location)) {
77-
result.put(location, this.loaders.load(location));
80+
result.put(location, this.loaders.load(loaderContext, location));
7881
}
7982
}
8083
return Collections.unmodifiableMap(result);

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoader.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,19 +40,21 @@ public interface ConfigDataLoader<L extends ConfigDataLocation> {
4040

4141
/**
4242
* Returns if the specified location can be loaded by this instance.
43+
* @param context the loader context
4344
* @param location the location to check.
4445
* @return if the location is supported by this loader
4546
*/
46-
default boolean isLoadable(L location) {
47+
default boolean isLoadable(ConfigDataLoaderContext context, L location) {
4748
return true;
4849
}
4950

5051
/**
5152
* Load {@link ConfigData} for the given location.
53+
* @param context the loader context
5254
* @param location the location to load
5355
* @return the loaded config data or {@code null} if the location should be skipped
5456
* @throws IOException on IO error
5557
*/
56-
ConfigData load(L location) throws IOException;
58+
ConfigData load(ConfigDataLoaderContext context, L location) throws IOException;
5759

5860
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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.context.config;
18+
19+
import org.springframework.boot.env.BootstrapRegistry;
20+
import org.springframework.boot.env.EnvironmentPostProcessor;
21+
22+
/**
23+
* Context provided to {@link ConfigDataLoader} methods.
24+
*
25+
* @author Phillip Webb
26+
* @since 2.4.0
27+
*/
28+
public interface ConfigDataLoaderContext {
29+
30+
/**
31+
* Provides access to the {@link BootstrapRegistry} shared across all
32+
* {@link EnvironmentPostProcessor EnvironmentPostProcessors}.
33+
* @return the bootstrap registry
34+
*/
35+
BootstrapRegistry getBootstrapRegistry();
36+
37+
}

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoaders.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,24 +80,25 @@ private Class<?> getLocationType(ConfigDataLoader<?> loader) {
8080
/**
8181
* Load {@link ConfigData} using the first appropriate {@link ConfigDataLoader}.
8282
* @param <L> the config data location type
83+
* @param context the loader context
8384
* @param location the location to load
8485
* @return the loaded {@link ConfigData}
8586
* @throws IOException on IO error
8687
*/
87-
<L extends ConfigDataLocation> ConfigData load(L location) throws IOException {
88-
ConfigDataLoader<L> loader = getLoader(location);
88+
<L extends ConfigDataLocation> ConfigData load(ConfigDataLoaderContext context, L location) throws IOException {
89+
ConfigDataLoader<L> loader = getLoader(context, location);
8990
this.logger.trace(LogMessage.of(() -> "Loading " + location + " using loader " + loader.getClass().getName()));
90-
return loader.load(location);
91+
return loader.load(context, location);
9192
}
9293

9394
@SuppressWarnings("unchecked")
94-
private <L extends ConfigDataLocation> ConfigDataLoader<L> getLoader(L location) {
95+
private <L extends ConfigDataLocation> ConfigDataLoader<L> getLoader(ConfigDataLoaderContext context, L location) {
9596
ConfigDataLoader<L> result = null;
9697
for (int i = 0; i < this.loaders.size(); i++) {
9798
ConfigDataLoader<?> candidate = this.loaders.get(i);
9899
if (this.locationTypes.get(i).isInstance(location)) {
99100
ConfigDataLoader<L> loader = (ConfigDataLoader<L>) candidate;
100-
if (loader.isLoadable(location)) {
101+
if (loader.isLoadable(context, location)) {
101102
if (result != null) {
102103
throw new IllegalStateException("Multiple loaders found for location " + location + " ["
103104
+ candidate.getClass().getName() + "," + result.getClass().getName() + "]");

0 commit comments

Comments
 (0)