Delay property source initialization till LoggingSystem is initialized

Previously, the initialization of StandardServletEnvironment's
property sources in SpringBootServletInitializer led to debug logging
calls being made before the LoggingSystem had been initialized. As a
result, the system's default configuration was used and, in the case
of Logback at least, the debug logging was output to System.out
in a war deployment.

This commit updates SpringBootServletInitializer to delay the
initialization of StandardServletEnvironment's property sources until
after the LoggingSystem has been initialized, but still in time for
active profiles to be configured via servlet context parameters
(see gh-9972).

Closes gh-13736
This commit is contained in:
Andy Wilkinson 2018-07-11 11:47:17 +01:00
parent 73a08dd668
commit 57ebdab2ab
2 changed files with 44 additions and 5 deletions

View File

@ -29,15 +29,19 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.builder.ParentContextApplicationContextInitializer;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.util.Assert;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ConfigurableWebEnvironment;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.StandardServletEnvironment;
/**
* An opinionated {@link WebApplicationInitializer} to run a {@link SpringApplication}
@ -104,9 +108,6 @@ public abstract class SpringBootServletInitializer implements WebApplicationInit
protected WebApplicationContext createRootApplicationContext(
ServletContext servletContext) {
SpringApplicationBuilder builder = createSpringApplicationBuilder();
StandardServletEnvironment environment = new StandardServletEnvironment();
environment.initPropertySources(servletContext, null);
builder.environment(environment);
builder.main(getClass());
ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
if (parent != null) {
@ -119,6 +120,7 @@ public abstract class SpringBootServletInitializer implements WebApplicationInit
new ServletContextApplicationContextInitializer(servletContext));
builder.contextClass(AnnotationConfigEmbeddedWebApplicationContext.class);
builder = configure(builder);
builder.listeners(new WebEnvironmentPropertySourceInitializer(servletContext));
SpringApplication application = builder.build();
if (application.getSources().isEmpty() && AnnotationUtils
.findAnnotation(getClass(), Configuration.class) != null) {
@ -177,4 +179,30 @@ public abstract class SpringBootServletInitializer implements WebApplicationInit
return builder;
}
private static final class WebEnvironmentPropertySourceInitializer
implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {
private final ServletContext servletContext;
private WebEnvironmentPropertySourceInitializer(ServletContext servletContext) {
this.servletContext = servletContext;
}
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment environment = event.getEnvironment();
if (environment instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) environment)
.initPropertySources(this.servletContext, null);
}
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@ -22,6 +22,7 @@ import java.util.Collections;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import org.junit.After;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@ -33,6 +34,7 @@ import org.springframework.boot.context.embedded.EmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.testutil.InternalOutputCapture;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
@ -59,10 +61,19 @@ public class SpringBootServletInitializerTests {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Rule
public InternalOutputCapture output = new InternalOutputCapture();
private ServletContext servletContext = new MockServletContext();
private SpringApplication application;
@After
public void verifyLoggingOutput() {
assertThat(this.output.toString())
.doesNotContain(StandardServletEnvironment.class.getSimpleName());
}
@Test
public void failsWithoutConfigure() throws Exception {
this.thrown.expect(IllegalStateException.class);