Drive EnvironmentPostProcessors from ConfigFileApplicationListener
Previously, ConfigFileApplicationListener was listed in spring.factories as both an EnvironmentPostProcessor and an ApplicationListener. This was problematic as ConfigFileApplicationListener is stateful and listing it twice lead to two separate instances with separate state. This commit restore ConfigFileApplicationListener to only being an ApplicationListener. The driving of EnvironmentPostProcessors that was performed by EnvironmentPostProcessingApplicationListener is now performed by ConfigFileApplicationListener which adds itself as an EnvironmentPostProcessor. This ensures that there’s only a single instance of ConfigFileApplicationListener, allowing its state to be managed correctly. Closes gh-4258
This commit is contained in:
parent
0adf037410
commit
833aac2b26
|
|
@ -24,12 +24,11 @@ import org.springframework.boot.Banner;
|
|||
import org.springframework.boot.ResourceBanner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.context.config.AnsiOutputApplicationListener;
|
||||
import org.springframework.boot.context.config.ConfigFileEnvironmentPostProcessor;
|
||||
import org.springframework.boot.context.config.ConfigFileApplicationListener;
|
||||
import org.springframework.boot.devtools.remote.client.RemoteClientConfiguration;
|
||||
import org.springframework.boot.devtools.restart.RestartInitializer;
|
||||
import org.springframework.boot.devtools.restart.RestartScopeInitializer;
|
||||
import org.springframework.boot.devtools.restart.Restarter;
|
||||
import org.springframework.boot.env.EnvironmentPostProcessingApplicationListener;
|
||||
import org.springframework.boot.logging.ClasspathLoggingApplicationListener;
|
||||
import org.springframework.boot.logging.LoggingApplicationListener;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
|
|
@ -73,8 +72,7 @@ public final class RemoteSpringApplication {
|
|||
private Collection<ApplicationListener<?>> getListeners() {
|
||||
List<ApplicationListener<?>> listeners = new ArrayList<ApplicationListener<?>>();
|
||||
listeners.add(new AnsiOutputApplicationListener());
|
||||
listeners.add(new ConfigFileEnvironmentPostProcessor());
|
||||
listeners.add(new EnvironmentPostProcessingApplicationListener());
|
||||
listeners.add(new ConfigFileApplicationListener());
|
||||
listeners.add(new ClasspathLoggingApplicationListener());
|
||||
listeners.add(new LoggingApplicationListener());
|
||||
listeners.add(new RemoteUrlPropertyExtractor());
|
||||
|
|
|
|||
|
|
@ -510,7 +510,7 @@ public class SpringApplication {
|
|||
* @param environment this application's environment
|
||||
* @param args arguments passed to the {@code run} method
|
||||
* @see #configureEnvironment(ConfigurableEnvironment, String[])
|
||||
* @see org.springframework.boot.context.config.ConfigFileEnvironmentPostProcessor
|
||||
* @see org.springframework.boot.context.config.ConfigFileApplicationListener
|
||||
*/
|
||||
protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
|
||||
environment.getActiveProfiles(); // ensure they are initialized
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import org.apache.commons.logging.Log;
|
|||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.context.config.ConfigFileEnvironmentPostProcessor;
|
||||
import org.springframework.boot.context.config.ConfigFileApplicationListener;
|
||||
import org.springframework.boot.env.EnvironmentPostProcessor;
|
||||
import org.springframework.boot.json.JsonParser;
|
||||
import org.springframework.boot.json.JsonParserFactory;
|
||||
|
|
@ -99,8 +99,8 @@ public class CloudFoundryVcapEnvironmentPostProcessor
|
|||
|
||||
private static final String VCAP_SERVICES = "VCAP_SERVICES";
|
||||
|
||||
// Before ConfigFileEnvironmentPostProcessor so values there can use these ones
|
||||
private int order = ConfigFileEnvironmentPostProcessor.DEFAULT_ORDER - 1;
|
||||
// Before ConfigFileApplicationListener so values there can use these ones
|
||||
private int order = ConfigFileApplicationListener.DEFAULT_ORDER - 1;
|
||||
|
||||
private final JsonParser parser = JsonParserFactory.getJsonParser();
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ import org.springframework.boot.ansi.AnsiOutput;
|
|||
import org.springframework.boot.ansi.AnsiOutput.Enabled;
|
||||
import org.springframework.boot.bind.RelaxedPropertyResolver;
|
||||
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
|
||||
import org.springframework.boot.env.EnvironmentPostProcessingApplicationListener;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
|
|
@ -52,9 +51,9 @@ public class AnsiOutputApplicationListener
|
|||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
// Apply after the EnvironmentPostProcessingApplicationListener has called all
|
||||
// Apply after ConfigFileApplicationListener has called all
|
||||
// EnvironmentPostProcessors
|
||||
return EnvironmentPostProcessingApplicationListener.ORDER + 1;
|
||||
return ConfigFileApplicationListener.DEFAULT_ORDER + 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,11 +34,13 @@ import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
|||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.bind.PropertiesConfigurationFactory;
|
||||
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
|
||||
import org.springframework.boot.context.event.ApplicationPreparedEvent;
|
||||
import org.springframework.boot.env.EnumerableCompositePropertySource;
|
||||
import org.springframework.boot.env.EnvironmentPostProcessor;
|
||||
import org.springframework.boot.env.PropertySourcesLoader;
|
||||
import org.springframework.boot.logging.DeferredLog;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
|
||||
|
|
@ -52,6 +54,7 @@ import org.springframework.core.env.PropertySource;
|
|||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
|
@ -90,8 +93,8 @@ import org.springframework.validation.BindException;
|
|||
* @author Stephane Nicoll
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class ConfigFileEnvironmentPostProcessor implements EnvironmentPostProcessor,
|
||||
ApplicationListener<ApplicationPreparedEvent>, Ordered {
|
||||
public class ConfigFileApplicationListener implements EnvironmentPostProcessor,
|
||||
ApplicationListener<ApplicationEvent>, Ordered {
|
||||
|
||||
private static final String DEFAULT_PROPERTIES = "defaultProperties";
|
||||
|
||||
|
|
@ -135,6 +138,29 @@ public class ConfigFileEnvironmentPostProcessor implements EnvironmentPostProces
|
|||
|
||||
private final ConversionService conversionService = new DefaultConversionService();
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ApplicationEvent event) {
|
||||
if (event instanceof ApplicationEnvironmentPreparedEvent) {
|
||||
onApplicationEnvironmentPreparedEvent(
|
||||
(ApplicationEnvironmentPreparedEvent) event);
|
||||
}
|
||||
if (event instanceof ApplicationPreparedEvent) {
|
||||
onApplicationPreparedEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
private void onApplicationEnvironmentPreparedEvent(
|
||||
ApplicationEnvironmentPreparedEvent event) {
|
||||
List<EnvironmentPostProcessor> postProcessors = SpringFactoriesLoader
|
||||
.loadFactories(EnvironmentPostProcessor.class,
|
||||
getClass().getClassLoader());
|
||||
postProcessors.add(this);
|
||||
for (EnvironmentPostProcessor postProcessor : postProcessors) {
|
||||
postProcessor.postProcessEnvironment(event.getEnvironment(),
|
||||
event.getSpringApplication());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessEnvironment(ConfigurableEnvironment environment,
|
||||
SpringApplication application) {
|
||||
|
|
@ -142,10 +168,9 @@ public class ConfigFileEnvironmentPostProcessor implements EnvironmentPostProces
|
|||
bindToSpringApplication(environment, application);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ApplicationPreparedEvent event) {
|
||||
this.logger.replayTo(ConfigFileEnvironmentPostProcessor.class);
|
||||
addPostProcessors(event.getApplicationContext());
|
||||
private void onApplicationPreparedEvent(ApplicationEvent event) {
|
||||
this.logger.replayTo(ConfigFileApplicationListener.class);
|
||||
addPostProcessors(((ApplicationPreparedEvent) event).getApplicationContext());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -268,7 +293,7 @@ public class ConfigFileEnvironmentPostProcessor implements EnvironmentPostProces
|
|||
*/
|
||||
private class Loader {
|
||||
|
||||
private final Log logger = ConfigFileEnvironmentPostProcessor.this.logger;
|
||||
private final Log logger = ConfigFileApplicationListener.this.logger;
|
||||
|
||||
private final ConfigurableEnvironment environment;
|
||||
|
||||
|
|
@ -417,18 +442,19 @@ public class ConfigFileEnvironmentPostProcessor implements EnvironmentPostProces
|
|||
|
||||
/**
|
||||
* Return the active profiles that have not been processed yet.
|
||||
* <p>If a profile is enabled via both {@link #ACTIVE_PROFILES_PROPERTY} and
|
||||
* <p>
|
||||
* If a profile is enabled via both {@link #ACTIVE_PROFILES_PROPERTY} and
|
||||
* {@link ConfigurableEnvironment#addActiveProfile(String)} it needs to be
|
||||
* filtered so that the {@link #ACTIVE_PROFILES_PROPERTY} value takes
|
||||
* precedence.
|
||||
* <p>Concretely, if the "cloud" profile is enabled via the environment,
|
||||
* it will take less precedence that any profile set via the
|
||||
* {@link #ACTIVE_PROFILES_PROPERTY}.
|
||||
* filtered so that the {@link #ACTIVE_PROFILES_PROPERTY} value takes precedence.
|
||||
* <p>
|
||||
* Concretely, if the "cloud" profile is enabled via the environment, it will take
|
||||
* less precedence that any profile set via the {@link #ACTIVE_PROFILES_PROPERTY}.
|
||||
* @param initialActiveProfiles the profiles that have been enabled via
|
||||
* {@link #ACTIVE_PROFILES_PROPERTY}
|
||||
* @return the additional profiles from the environment to enable
|
||||
*/
|
||||
private List<String> filterEnvironmentProfiles(Set<String> initialActiveProfiles) {
|
||||
private List<String> filterEnvironmentProfiles(
|
||||
Set<String> initialActiveProfiles) {
|
||||
List<String> additionalProfiles = new ArrayList<String>();
|
||||
for (String profile : this.environment.getActiveProfiles()) {
|
||||
if (!initialActiveProfiles.contains(profile)) {
|
||||
|
|
@ -501,7 +527,7 @@ public class ConfigFileEnvironmentPostProcessor implements EnvironmentPostProces
|
|||
}
|
||||
}
|
||||
locations.addAll(
|
||||
asResolvedSet(ConfigFileEnvironmentPostProcessor.this.searchLocations,
|
||||
asResolvedSet(ConfigFileApplicationListener.this.searchLocations,
|
||||
DEFAULT_SEARCH_LOCATIONS));
|
||||
return locations;
|
||||
}
|
||||
|
|
@ -511,8 +537,7 @@ public class ConfigFileEnvironmentPostProcessor implements EnvironmentPostProces
|
|||
return asResolvedSet(this.environment.getProperty(CONFIG_NAME_PROPERTY),
|
||||
null);
|
||||
}
|
||||
return asResolvedSet(ConfigFileEnvironmentPostProcessor.this.names,
|
||||
DEFAULT_NAMES);
|
||||
return asResolvedSet(ConfigFileApplicationListener.this.names, DEFAULT_NAMES);
|
||||
}
|
||||
|
||||
private Set<String> asResolvedSet(String value, String fallback) {
|
||||
|
|
@ -18,6 +18,6 @@
|
|||
* External configuration support allowing 'application.properties' to be loaded and used
|
||||
* within a Spring Boot application.
|
||||
*
|
||||
* @see org.springframework.boot.context.config.ConfigFileEnvironmentPostProcessor
|
||||
* @see org.springframework.boot.context.config.ConfigFileApplicationListener
|
||||
*/
|
||||
package org.springframework.boot.context.config;
|
||||
|
|
|
|||
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2015 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
|
||||
*
|
||||
* http://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.List;
|
||||
|
||||
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
|
||||
/**
|
||||
* An {@link ApplicationListener} that responds to an
|
||||
* {@link ApplicationEnvironmentPreparedEvent} and calls all
|
||||
* {@link EnvironmentPostProcessor EnvironmentPostProcessors} that are available via
|
||||
* {@code spring.factories}.
|
||||
* <p>
|
||||
* Post-processors are called in the order defined by
|
||||
* {@link AnnotationAwareOrderComparator}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 1.3.0
|
||||
* @see SpringFactoriesLoader#loadFactories(Class, ClassLoader)
|
||||
*/
|
||||
public class EnvironmentPostProcessingApplicationListener
|
||||
implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {
|
||||
|
||||
/**
|
||||
* The order for the {@link EnvironmentPostProcessingApplicationListener}.
|
||||
*/
|
||||
public static final int ORDER = Ordered.HIGHEST_PRECEDENCE + 10;
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
|
||||
List<EnvironmentPostProcessor> postProcessors = SpringFactoriesLoader
|
||||
.loadFactories(EnvironmentPostProcessor.class,
|
||||
getClass().getClassLoader());
|
||||
for (EnvironmentPostProcessor postProcessor : postProcessors) {
|
||||
postProcessor.postProcessEnvironment(event.getEnvironment(),
|
||||
event.getSpringApplication());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return ORDER;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
package org.springframework.boot.test;
|
||||
|
||||
import org.springframework.boot.context.config.ConfigFileEnvironmentPostProcessor;
|
||||
import org.springframework.boot.context.config.ConfigFileApplicationListener;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
|
@ -27,14 +27,14 @@ import org.springframework.test.context.ContextConfiguration;
|
|||
* {@literal application.properties}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @see ConfigFileEnvironmentPostProcessor
|
||||
* @see ConfigFileApplicationListener
|
||||
*/
|
||||
public class ConfigFileApplicationContextInitializer
|
||||
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
|
||||
|
||||
@Override
|
||||
public void initialize(final ConfigurableApplicationContext applicationContext) {
|
||||
new ConfigFileEnvironmentPostProcessor() {
|
||||
new ConfigFileApplicationListener() {
|
||||
public void apply() {
|
||||
addPropertySources(applicationContext.getEnvironment(),
|
||||
applicationContext);
|
||||
|
|
|
|||
|
|
@ -19,14 +19,12 @@ org.springframework.context.ApplicationListener=\
|
|||
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
|
||||
org.springframework.boot.context.FileEncodingApplicationListener,\
|
||||
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
|
||||
org.springframework.boot.context.config.ConfigFileEnvironmentPostProcessor,\
|
||||
org.springframework.boot.context.config.ConfigFileApplicationListener,\
|
||||
org.springframework.boot.context.config.DelegatingApplicationListener,\
|
||||
org.springframework.boot.env.EnvironmentPostProcessingApplicationListener,\
|
||||
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,\
|
||||
org.springframework.boot.logging.ClasspathLoggingApplicationListener,\
|
||||
org.springframework.boot.logging.LoggingApplicationListener
|
||||
|
||||
# Environment Post Processors
|
||||
org.springframework.boot.env.EnvironmentPostProcessor=\
|
||||
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
|
||||
org.springframework.boot.context.config.ConfigFileEnvironmentPostProcessor
|
||||
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ import org.slf4j.LoggerFactory;
|
|||
|
||||
import org.springframework.boot.Banner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.context.config.ConfigFileEnvironmentPostProcessor.ConfigurationPropertySources;
|
||||
import org.springframework.boot.context.config.ConfigFileApplicationListener.ConfigurationPropertySources;
|
||||
import org.springframework.boot.context.event.ApplicationPreparedEvent;
|
||||
import org.springframework.boot.env.EnumerableCompositePropertySource;
|
||||
import org.springframework.boot.test.EnvironmentTestUtils;
|
||||
|
|
@ -74,18 +74,18 @@ import static org.junit.Assert.assertThat;
|
|||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Tests for {@link ConfigFileEnvironmentPostProcessor}.
|
||||
* Tests for {@link ConfigFileApplicationListener}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class ConfigFileEnvironmentPostProcessorTests {
|
||||
public class ConfigFileApplicationListenerTests {
|
||||
|
||||
private final StandardEnvironment environment = new StandardEnvironment();
|
||||
|
||||
private final SpringApplication application = new SpringApplication();
|
||||
|
||||
private final ConfigFileEnvironmentPostProcessor initializer = new ConfigFileEnvironmentPostProcessor();
|
||||
private final ConfigFileApplicationListener initializer = new ConfigFileApplicationListener();
|
||||
|
||||
@Rule
|
||||
public ExpectedException expected = ExpectedException.none();
|
||||
Loading…
Reference in New Issue