Merge branch '2.7.x'

Closes gh-32827
This commit is contained in:
Phillip Webb 2022-10-20 15:20:15 -07:00
commit 7db93aeb90
12 changed files with 189 additions and 68 deletions

View File

@ -18,12 +18,10 @@ package org.springframework.boot;
import java.util.function.Supplier;
import org.springframework.aot.AotDetector;
import org.springframework.beans.BeanUtils;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
/**
* Strategy interface for creating the {@link ConfigurableApplicationContext} used by a
@ -42,23 +40,32 @@ public interface ApplicationContextFactory {
* A default {@link ApplicationContextFactory} implementation that will create an
* appropriate context for the {@link WebApplicationType}.
*/
ApplicationContextFactory DEFAULT = (webApplicationType) -> {
try {
for (ApplicationContextFactory candidate : SpringFactoriesLoader
.loadFactories(ApplicationContextFactory.class, ApplicationContextFactory.class.getClassLoader())) {
ConfigurableApplicationContext context = candidate.create(webApplicationType);
if (context != null) {
return context;
}
}
return AotDetector.useGeneratedArtifacts() ? new GenericApplicationContext()
: new AnnotationConfigApplicationContext();
}
catch (Exception ex) {
throw new IllegalStateException("Unable create a default ApplicationContext instance, "
+ "you may need a custom ApplicationContextFactory", ex);
}
};
ApplicationContextFactory DEFAULT = new DefaultApplicationContextFactory();
/**
* Return the {@link Environment} type expected to be set on the
* {@link #create(WebApplicationType) created} application context. The result of this
* method can be used to convert an existing environment instance to the correct type.
* @param webApplicationType the web application type
* @return the expected application context type or {@code null} to use the default
* @since 2.6.14
*/
default Class<? extends ConfigurableEnvironment> getEnvironmentType(WebApplicationType webApplicationType) {
return null;
}
/**
* Create a new {@link Environment} to be set on the
* {@link #create(WebApplicationType) created} application context. The result of this
* method must match the type returned by
* {@link #getEnvironmentType(WebApplicationType)}.
* @param webApplicationType the web application type
* @return an environment instance or {@code null} to use the default
* @since 2.6.14
*/
default ConfigurableEnvironment createEnvironment(WebApplicationType webApplicationType) {
return null;
}
/**
* Creates the {@link ConfigurableApplicationContext application context} for a

View File

@ -0,0 +1,78 @@
/*
* Copyright 2012-2022 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
*
* https://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;
import java.util.function.BiFunction;
import java.util.function.Supplier;
import org.springframework.aot.AotDetector;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.support.SpringFactoriesLoader;
/**
* Default {@link ApplicationContextFactory} implementation that will create an
* appropriate context for the {@link WebApplicationType}.
*
* @author Phillip Webb
*/
class DefaultApplicationContextFactory implements ApplicationContextFactory {
@Override
public Class<? extends ConfigurableEnvironment> getEnvironmentType(WebApplicationType webApplicationType) {
return getFromSpringFactories(webApplicationType, ApplicationContextFactory::getEnvironmentType, null);
}
@Override
public ConfigurableEnvironment createEnvironment(WebApplicationType webApplicationType) {
return getFromSpringFactories(webApplicationType, ApplicationContextFactory::createEnvironment, null);
}
@Override
public ConfigurableApplicationContext create(WebApplicationType webApplicationType) {
try {
return getFromSpringFactories(webApplicationType, ApplicationContextFactory::create,
this::createDefaultApplicationContext);
}
catch (Exception ex) {
throw new IllegalStateException("Unable create a default ApplicationContext instance, "
+ "you may need a custom ApplicationContextFactory", ex);
}
}
private ConfigurableApplicationContext createDefaultApplicationContext() {
if (!AotDetector.useGeneratedArtifacts()) {
return new AnnotationConfigApplicationContext();
}
return new GenericApplicationContext();
}
private <T> T getFromSpringFactories(WebApplicationType webApplicationType,
BiFunction<ApplicationContextFactory, WebApplicationType, T> action, Supplier<T> defaultResult) {
for (ApplicationContextFactory candidate : SpringFactoriesLoader.loadFactories(ApplicationContextFactory.class,
getClass().getClassLoader())) {
T result = action.apply(candidate, webApplicationType);
if (result != null) {
return result;
}
}
return (defaultResult != null) ? defaultResult.get() : null;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2022 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.
@ -16,6 +16,7 @@
package org.springframework.boot;
import java.lang.reflect.Constructor;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
@ -26,6 +27,7 @@ import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.context.support.StandardServletEnvironment;
/**
@ -68,33 +70,35 @@ final class EnvironmentConverter {
* @param type the type to convert the Environment to
* @return the converted Environment
*/
StandardEnvironment convertEnvironmentIfNecessary(ConfigurableEnvironment environment,
Class<? extends StandardEnvironment> type) {
ConfigurableEnvironment convertEnvironmentIfNecessary(ConfigurableEnvironment environment,
Class<? extends ConfigurableEnvironment> type) {
if (type.equals(environment.getClass())) {
return (StandardEnvironment) environment;
return environment;
}
return convertEnvironment(environment, type);
}
private StandardEnvironment convertEnvironment(ConfigurableEnvironment environment,
Class<? extends StandardEnvironment> type) {
StandardEnvironment result = createEnvironment(type);
private ConfigurableEnvironment convertEnvironment(ConfigurableEnvironment environment,
Class<? extends ConfigurableEnvironment> type) {
ConfigurableEnvironment result = createEnvironment(type);
result.setActiveProfiles(environment.getActiveProfiles());
result.setConversionService(environment.getConversionService());
copyPropertySources(environment, result);
return result;
}
private StandardEnvironment createEnvironment(Class<? extends StandardEnvironment> type) {
private ConfigurableEnvironment createEnvironment(Class<? extends ConfigurableEnvironment> type) {
try {
return type.getDeclaredConstructor().newInstance();
Constructor<? extends ConfigurableEnvironment> constructor = type.getDeclaredConstructor();
ReflectionUtils.makeAccessible(constructor);
return constructor.newInstance();
}
catch (Exception ex) {
return new StandardEnvironment();
return new ApplicationEnvironment();
}
}
private void copyPropertySources(ConfigurableEnvironment source, StandardEnvironment target) {
private void copyPropertySources(ConfigurableEnvironment source, ConfigurableEnvironment target) {
removePropertySources(target.getPropertySources(), isServletEnvironment(target.getClass(), this.classLoader));
for (PropertySource<?> propertySource : source.getPropertySources()) {
if (!SERVLET_ENVIRONMENT_SOURCE_NAMES.contains(propertySource.getName())) {

View File

@ -79,7 +79,6 @@ import org.springframework.core.env.Environment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.SimpleCommandLinePropertySource;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.SpringFactoriesLoader;
@ -365,12 +364,16 @@ public class SpringApplication {
return environment;
}
private Class<? extends StandardEnvironment> deduceEnvironmentClass() {
return switch (this.webApplicationType) {
case SERVLET -> ApplicationServletEnvironment.class;
case REACTIVE -> ApplicationReactiveWebEnvironment.class;
default -> ApplicationEnvironment.class;
};
private Class<? extends ConfigurableEnvironment> deduceEnvironmentClass() {
Class<? extends ConfigurableEnvironment> environmentType = this.applicationContextFactory
.getEnvironmentType(this.webApplicationType);
if (environmentType == null && this.applicationContextFactory != ApplicationContextFactory.DEFAULT) {
environmentType = ApplicationContextFactory.DEFAULT.getEnvironmentType(this.webApplicationType);
}
if (environmentType == null) {
return ApplicationEnvironment.class;
}
return environmentType;
}
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
@ -462,14 +465,11 @@ public class SpringApplication {
if (this.environment != null) {
return this.environment;
}
switch (this.webApplicationType) {
case SERVLET:
return new ApplicationServletEnvironment();
case REACTIVE:
return new ApplicationReactiveWebEnvironment();
default:
return new ApplicationEnvironment();
ConfigurableEnvironment environment = this.applicationContextFactory.createEnvironment(this.webApplicationType);
if (environment == null && this.applicationContextFactory != ApplicationContextFactory.DEFAULT) {
environment = ApplicationContextFactory.DEFAULT.createEnvironment(this.webApplicationType);
}
return (environment != null) ? environment : new ApplicationEnvironment();
}
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2021 the original author or authors.
* Copyright 2012-2022 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.
@ -14,10 +14,10 @@
* limitations under the License.
*/
package org.springframework.boot;
package org.springframework.boot.web.reactive.context;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
import org.springframework.boot.web.reactive.context.StandardReactiveWebEnvironment;
import org.springframework.core.env.ConfigurablePropertyResolver;
import org.springframework.core.env.MutablePropertySources;

View File

@ -20,6 +20,7 @@ import org.springframework.aot.AotDetector;
import org.springframework.boot.ApplicationContextFactory;
import org.springframework.boot.WebApplicationType;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
/**
* {@link ApplicationContextFactory} registered in {@code spring.factories} to support
@ -31,6 +32,16 @@ import org.springframework.context.ConfigurableApplicationContext;
*/
class ReactiveWebServerApplicationContextFactory implements ApplicationContextFactory {
@Override
public Class<? extends ConfigurableEnvironment> getEnvironmentType(WebApplicationType webApplicationType) {
return (webApplicationType != WebApplicationType.REACTIVE) ? null : ApplicationReactiveWebEnvironment.class;
}
@Override
public ConfigurableEnvironment createEnvironment(WebApplicationType webApplicationType) {
return (webApplicationType != WebApplicationType.REACTIVE) ? null : new ApplicationReactiveWebEnvironment();
}
@Override
public ConfigurableApplicationContext create(WebApplicationType webApplicationType) {
return (webApplicationType != WebApplicationType.REACTIVE) ? null : createContext();

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2021 the original author or authors.
* Copyright 2012-2022 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.
@ -14,8 +14,9 @@
* limitations under the License.
*/
package org.springframework.boot;
package org.springframework.boot.web.servlet.context;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
import org.springframework.core.env.ConfigurablePropertyResolver;
import org.springframework.core.env.MutablePropertySources;

View File

@ -20,6 +20,7 @@ import org.springframework.aot.AotDetector;
import org.springframework.boot.ApplicationContextFactory;
import org.springframework.boot.WebApplicationType;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
/**
* {@link ApplicationContextFactory} registered in {@code spring.factories} to support
@ -31,6 +32,16 @@ import org.springframework.context.ConfigurableApplicationContext;
*/
class ServletWebServerApplicationContextFactory implements ApplicationContextFactory {
@Override
public Class<? extends ConfigurableEnvironment> getEnvironmentType(WebApplicationType webApplicationType) {
return (webApplicationType != WebApplicationType.SERVLET) ? null : ApplicationServletEnvironment.class;
}
@Override
public ConfigurableEnvironment createEnvironment(WebApplicationType webApplicationType) {
return (webApplicationType != WebApplicationType.SERVLET) ? null : new ApplicationServletEnvironment();
}
@Override
public ConfigurableApplicationContext create(WebApplicationType webApplicationType) {
return (webApplicationType != WebApplicationType.SERVLET) ? null : createContext();

View File

@ -23,6 +23,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.mock.env.MockEnvironment;
@ -46,7 +47,7 @@ class EnvironmentConverterTests {
void convertedEnvironmentHasSameActiveProfiles() {
AbstractEnvironment originalEnvironment = new MockEnvironment();
originalEnvironment.setActiveProfiles("activeProfile1", "activeProfile2");
StandardEnvironment convertedEnvironment = this.environmentConverter
ConfigurableEnvironment convertedEnvironment = this.environmentConverter
.convertEnvironmentIfNecessary(originalEnvironment, StandardEnvironment.class);
assertThat(convertedEnvironment.getActiveProfiles()).containsExactly("activeProfile1", "activeProfile2");
}
@ -56,7 +57,7 @@ class EnvironmentConverterTests {
AbstractEnvironment originalEnvironment = new MockEnvironment();
ConfigurableConversionService conversionService = mock(ConfigurableConversionService.class);
originalEnvironment.setConversionService(conversionService);
StandardEnvironment convertedEnvironment = this.environmentConverter
ConfigurableEnvironment convertedEnvironment = this.environmentConverter
.convertEnvironmentIfNecessary(originalEnvironment, StandardEnvironment.class);
assertThat(convertedEnvironment.getConversionService()).isEqualTo(conversionService);
}
@ -64,7 +65,7 @@ class EnvironmentConverterTests {
@Test
void envClassSameShouldReturnEnvironmentUnconverted() {
StandardEnvironment standardEnvironment = new StandardEnvironment();
StandardEnvironment convertedEnvironment = this.environmentConverter
ConfigurableEnvironment convertedEnvironment = this.environmentConverter
.convertEnvironmentIfNecessary(standardEnvironment, StandardEnvironment.class);
assertThat(convertedEnvironment).isSameAs(standardEnvironment);
}
@ -72,7 +73,7 @@ class EnvironmentConverterTests {
@Test
void standardServletEnvironmentIsConverted() {
StandardServletEnvironment standardServletEnvironment = new StandardServletEnvironment();
StandardEnvironment convertedEnvironment = this.environmentConverter
ConfigurableEnvironment convertedEnvironment = this.environmentConverter
.convertEnvironmentIfNecessary(standardServletEnvironment, StandardEnvironment.class);
assertThat(convertedEnvironment).isNotSameAs(standardServletEnvironment);
}
@ -80,7 +81,7 @@ class EnvironmentConverterTests {
@Test
void servletPropertySourcesAreNotCopiedOverIfNotWebEnvironment() {
StandardServletEnvironment standardServletEnvironment = new StandardServletEnvironment();
StandardEnvironment convertedEnvironment = this.environmentConverter
ConfigurableEnvironment convertedEnvironment = this.environmentConverter
.convertEnvironmentIfNecessary(standardServletEnvironment, StandardEnvironment.class);
assertThat(convertedEnvironment).isNotSameAs(standardServletEnvironment);
Set<String> names = new HashSet<>();
@ -95,7 +96,7 @@ class EnvironmentConverterTests {
@Test
void envClassSameShouldReturnEnvironmentUnconvertedEvenForWeb() {
StandardServletEnvironment standardServletEnvironment = new StandardServletEnvironment();
StandardEnvironment convertedEnvironment = this.environmentConverter
ConfigurableEnvironment convertedEnvironment = this.environmentConverter
.convertEnvironmentIfNecessary(standardServletEnvironment, StandardServletEnvironment.class);
assertThat(convertedEnvironment).isSameAs(standardServletEnvironment);
}
@ -103,7 +104,7 @@ class EnvironmentConverterTests {
@Test
void servletPropertySourcesArePresentWhenTypeToConvertIsWeb() {
StandardEnvironment standardEnvironment = new StandardEnvironment();
StandardEnvironment convertedEnvironment = this.environmentConverter
ConfigurableEnvironment convertedEnvironment = this.environmentConverter
.convertEnvironmentIfNecessary(standardEnvironment, StandardServletEnvironment.class);
assertThat(convertedEnvironment).isNotSameAs(standardEnvironment);
Set<String> names = new HashSet<>();

View File

@ -76,6 +76,7 @@ import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext;
import org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext;
import org.springframework.boot.web.reactive.context.StandardReactiveWebEnvironment;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
@ -432,7 +433,8 @@ class SpringApplicationTests {
SpringApplication application = new SpringApplication(ExampleWebConfig.class);
application.setWebApplicationType(WebApplicationType.SERVLET);
this.context = application.run();
assertThat(this.context.getEnvironment()).isInstanceOf(ApplicationServletEnvironment.class);
assertThat(this.context.getEnvironment()).isInstanceOf(StandardServletEnvironment.class);
assertThat(this.context.getEnvironment().getClass().getName()).endsWith("ApplicationServletEnvironment");
}
@Test
@ -440,7 +442,8 @@ class SpringApplicationTests {
SpringApplication application = new SpringApplication(ExampleReactiveWebConfig.class);
application.setWebApplicationType(WebApplicationType.REACTIVE);
this.context = application.run();
assertThat(this.context.getEnvironment()).isInstanceOf(ApplicationReactiveWebEnvironment.class);
assertThat(this.context.getEnvironment()).isInstanceOf(StandardReactiveWebEnvironment.class);
assertThat(this.context.getEnvironment().getClass().getName()).endsWith("ApplicationReactiveWebEnvironment");
}
@Test
@ -1015,7 +1018,7 @@ class SpringApplicationTests {
void webApplicationSwitchedOffInListener() {
TestSpringApplication application = new TestSpringApplication(ExampleConfig.class);
application.addListeners((ApplicationListener<ApplicationEnvironmentPreparedEvent>) (event) -> {
assertThat(event.getEnvironment()).isInstanceOf(ApplicationServletEnvironment.class);
assertThat(event.getEnvironment().getClass().getName()).endsWith("ApplicationServletEnvironment");
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(event.getEnvironment(), "foo=bar");
event.getSpringApplication().setWebApplicationType(WebApplicationType.NONE);
});
@ -1041,7 +1044,8 @@ class SpringApplicationTests {
ConfigurableApplicationContext context = new SpringApplication(ExampleWebConfig.class)
.run("--spring.main.web-application-type=servlet");
assertThat(context).isInstanceOf(WebApplicationContext.class);
assertThat(context.getEnvironment()).isInstanceOf(ApplicationServletEnvironment.class);
assertThat(context.getEnvironment()).isInstanceOf(StandardServletEnvironment.class);
assertThat(context.getEnvironment().getClass().getName()).endsWith("ApplicationServletEnvironment");
}
@Test
@ -1049,7 +1053,8 @@ class SpringApplicationTests {
ConfigurableApplicationContext context = new SpringApplication(ExampleReactiveWebConfig.class)
.run("--spring.main.web-application-type=reactive");
assertThat(context).isInstanceOf(ReactiveWebApplicationContext.class);
assertThat(context.getEnvironment()).isInstanceOf(ApplicationReactiveWebEnvironment.class);
assertThat(context.getEnvironment()).isInstanceOf(StandardReactiveWebEnvironment.class);
assertThat(context.getEnvironment().getClass().getName()).endsWith("ApplicationReactiveWebEnvironment");
}
@Test
@ -1057,7 +1062,8 @@ class SpringApplicationTests {
ConfigurableApplicationContext context = new SpringApplication(ExampleReactiveWebConfig.class)
.run("--spring.profiles.active=withwebapplicationtype");
assertThat(context).isInstanceOf(ReactiveWebApplicationContext.class);
assertThat(context.getEnvironment()).isInstanceOf(ApplicationReactiveWebEnvironment.class);
assertThat(context.getEnvironment()).isInstanceOf(StandardReactiveWebEnvironment.class);
assertThat(context.getEnvironment().getClass().getName()).endsWith("ApplicationReactiveWebEnvironment");
}
@Test

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2021 the original author or authors.
* Copyright 2012-2022 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.
@ -14,8 +14,9 @@
* limitations under the License.
*/
package org.springframework.boot;
package org.springframework.boot.web.reactive.context;
import org.springframework.boot.AbstractApplicationEnvironmentTests;
import org.springframework.core.env.StandardEnvironment;
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2021 the original author or authors.
* Copyright 2012-2022 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.
@ -14,8 +14,9 @@
* limitations under the License.
*/
package org.springframework.boot;
package org.springframework.boot.web.servlet.context;
import org.springframework.boot.AbstractApplicationEnvironmentTests;
import org.springframework.core.env.StandardEnvironment;
/**