Skip to content

Commit 858b092

Browse files
committed
Make servlet context property source available before refresh
Previously, when deploying a Spring Boot application to a container, the servlet context property source was not fully initialised until the context was refreshed. This led to a problem where a value from a property source with lower precedence would be seen during the early stages of the application starting. Once the servlet context property source had been initialized, its value for the property would then become visible effectively making it appear as if the property's value had changed during startup. This led to a specific problem with determining active profiles. If spring.profiles.active was set both in JNDI and via the servlet context both profiles would end up being active, rather than the more intuitive behaviour of the profiles made active via the servlet context overriding those made active via JNDI. This commit updates SpringBootServletInitializer so that it explicitly creates the StandardServletEnvironment and initializes its property sources using the servlet context. This is done before the application is created and run, thereby ensuring that the servlet context property source is available throughout the application's startup. Closes gh-9972
1 parent 71dbbc0 commit 858b092

File tree

3 files changed

+63
-61
lines changed

3 files changed

+63
-61
lines changed

spring-boot/src/main/java/org/springframework/boot/web/support/ServletContextApplicationListener.java

Lines changed: 0 additions & 56 deletions
This file was deleted.

spring-boot/src/main/java/org/springframework/boot/web/support/SpringBootServletInitializer.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.springframework.web.WebApplicationInitializer;
3838
import org.springframework.web.context.ContextLoaderListener;
3939
import org.springframework.web.context.WebApplicationContext;
40+
import org.springframework.web.context.support.StandardServletEnvironment;
4041

4142
/**
4243
* An opinionated {@link WebApplicationInitializer} to run a {@link SpringApplication}
@@ -103,6 +104,9 @@ public void contextInitialized(ServletContextEvent event) {
103104
protected WebApplicationContext createRootApplicationContext(
104105
ServletContext servletContext) {
105106
SpringApplicationBuilder builder = createSpringApplicationBuilder();
107+
StandardServletEnvironment environment = new StandardServletEnvironment();
108+
environment.initPropertySources(servletContext, null);
109+
builder.environment(environment);
106110
builder.main(getClass());
107111
ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
108112
if (parent != null) {
@@ -113,7 +117,6 @@ protected WebApplicationContext createRootApplicationContext(
113117
}
114118
builder.initializers(
115119
new ServletContextApplicationContextInitializer(servletContext));
116-
builder.listeners(new ServletContextApplicationListener(servletContext));
117120
builder.contextClass(AnnotationConfigEmbeddedWebApplicationContext.class);
118121
builder = configure(builder);
119122
SpringApplication application = builder.build();

spring-boot/src/test/java/org/springframework/boot/web/support/SpringBootServletInitializerTests.java

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

1717
package org.springframework.boot.web.support;
1818

19+
import java.util.Arrays;
20+
import java.util.Collections;
21+
1922
import javax.servlet.ServletContext;
2023
import javax.servlet.ServletException;
2124

@@ -29,15 +32,21 @@
2932
import org.springframework.boot.context.embedded.EmbeddedServletContainer;
3033
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
3134
import org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory;
35+
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
3236
import org.springframework.boot.web.servlet.ServletContextInitializer;
37+
import org.springframework.context.ApplicationListener;
3338
import org.springframework.context.ConfigurableApplicationContext;
3439
import org.springframework.context.annotation.Bean;
3540
import org.springframework.context.annotation.Configuration;
3641
import org.springframework.context.support.AbstractApplicationContext;
42+
import org.springframework.core.env.PropertySource;
3743
import org.springframework.mock.web.MockServletContext;
3844
import org.springframework.web.context.WebApplicationContext;
45+
import org.springframework.web.context.support.StandardServletEnvironment;
3946

4047
import static org.assertj.core.api.Assertions.assertThat;
48+
import static org.mockito.BDDMockito.given;
49+
import static org.mockito.Mockito.mock;
4150

4251
/**
4352
* Tests for {@link SpringBootServletInitializer}.
@@ -135,10 +144,43 @@ public void executableWarThatUsesServletInitializerDoesNotHaveErrorPageFilterCon
135144
}
136145

137146
@Test
138-
public void servletContextApplicationListenerIsAdded() {
139-
new WithConfiguredSource().createRootApplicationContext(this.servletContext);
140-
assertThat(this.application.getListeners())
141-
.hasAtLeastOneElementOfType(ServletContextApplicationListener.class);
147+
public void servletContextPropertySourceIsAvailablePriorToRefresh()
148+
throws ServletException {
149+
ServletContext servletContext = mock(ServletContext.class);
150+
given(servletContext.getInitParameterNames()).willReturn(
151+
Collections.enumeration(Arrays.asList("spring.profiles.active")));
152+
given(servletContext.getInitParameter("spring.profiles.active"))
153+
.willReturn("from-servlet-context");
154+
given(servletContext.getAttributeNames())
155+
.willReturn(Collections.enumeration(Collections.<String>emptyList()));
156+
WebApplicationContext context = null;
157+
try {
158+
context = new PropertySourceVerifyingSpringBootServletInitializer()
159+
.createRootApplicationContext(servletContext);
160+
assertThat(context.getEnvironment().getActiveProfiles())
161+
.containsExactly("from-servlet-context");
162+
}
163+
finally {
164+
if (context instanceof ConfigurableApplicationContext) {
165+
((ConfigurableApplicationContext) context).close();
166+
}
167+
}
168+
}
169+
170+
private static class PropertySourceVerifyingSpringBootServletInitializer
171+
extends SpringBootServletInitializer {
172+
173+
@Override
174+
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
175+
return builder.sources(TestApp.class)
176+
.listeners(new PropertySourceVerifyingApplicationListener());
177+
}
178+
179+
}
180+
181+
@Configuration
182+
static class TestApp {
183+
142184
}
143185

144186
private class MockSpringBootServletInitializer extends SpringBootServletInitializer {
@@ -221,4 +263,17 @@ public SpringApplication build() {
221263

222264
}
223265

266+
private static final class PropertySourceVerifyingApplicationListener
267+
implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
268+
269+
@Override
270+
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
271+
PropertySource<?> propertySource = event.getEnvironment().getPropertySources()
272+
.get(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME);
273+
assertThat(propertySource.getProperty("spring.profiles.active"))
274+
.isEqualTo("from-servlet-context");
275+
}
276+
277+
}
278+
224279
}

0 commit comments

Comments
 (0)