Merge branch '4.0.x' into 4.1.x

Closes gh-51730
This commit is contained in:
Stéphane Nicoll
2026-09-14 11:42:14 +02:00
2 changed files with 107 additions and 11 deletions
@@ -55,9 +55,9 @@ public class DevToolsPropertyDefaultsPostProcessor implements EnvironmentPostPro
private static final String WEB_LOGGING = "logging.level.web";
private static final String[] WEB_ENVIRONMENT_CLASSES = {
"org.springframework.web.context.ConfigurableWebEnvironment",
"org.springframework.boot.web.reactive.context.ConfigurableReactiveWebEnvironment" };
private static final WebEnvironmentClassResolver[] WEB_ENVIRONMENT_CLASS_RESOLVERS = new WebEnvironmentClassResolver[] {
WebEnvironmentClassResolver.fromClassName("org.springframework.web.context.ConfigurableWebEnvironment"),
(classloader) -> org.springframework.boot.web.context.reactive.ConfigurableReactiveWebEnvironment.class };
private static final Map<String, Object> PROPERTIES;
@@ -112,8 +112,9 @@ public class DevToolsPropertyDefaultsPostProcessor implements EnvironmentPostPro
}
private boolean isWebApplication(Environment environment) {
for (String candidate : WEB_ENVIRONMENT_CLASSES) {
Class<?> environmentClass = resolveClassName(candidate, environment.getClass().getClassLoader());
ClassLoader classLoader = environment.getClass().getClassLoader();
for (WebEnvironmentClassResolver resolver : WEB_ENVIRONMENT_CLASS_RESOLVERS) {
Class<?> environmentClass = resolver.resolve(classLoader);
if (environmentClass != null && environmentClass.isInstance(environment)) {
return true;
}
@@ -121,13 +122,21 @@ public class DevToolsPropertyDefaultsPostProcessor implements EnvironmentPostPro
return false;
}
private @Nullable Class<?> resolveClassName(String candidate, ClassLoader classLoader) {
try {
return ClassUtils.resolveClassName(candidate, classLoader);
}
catch (IllegalArgumentException ex) {
return null;
private interface WebEnvironmentClassResolver {
@Nullable Class<?> resolve(ClassLoader classLoader);
static WebEnvironmentClassResolver fromClassName(String candidate) {
return (classLoader) -> {
try {
return ClassUtils.resolveClassName(candidate, classLoader);
}
catch (IllegalArgumentException ex) {
return null;
}
};
}
}
}
@@ -0,0 +1,87 @@
/*
* Copyright 2012-present 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.devtools.env;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.logging.DeferredLog;
import org.springframework.boot.test.system.CapturedOutput;
import org.springframework.boot.test.system.OutputCaptureExtension;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.boot.web.context.reactive.StandardReactiveWebEnvironment;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.context.support.StandardServletEnvironment;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link DevToolsPropertyDefaultsPostProcessor}.
*
* @author ohchanKyu
*/
@ExtendWith(OutputCaptureExtension.class)
class DevToolsPropertyDefaultsPostProcessorTests {
private static final String WEB_LOGGING_HINT = "For additional web related logging consider setting the "
+ "'logging.level.web' property to 'DEBUG'";
@Test
void postProcessWhenServletWebEnvironmentLogsWebLoggingHint(CapturedOutput output) throws Exception {
postProcess(new StandardServletEnvironment());
assertThat(output).contains(WEB_LOGGING_HINT);
}
@Test
void postProcessWhenReactiveWebEnvironmentLogsWebLoggingHint(CapturedOutput output) throws Exception {
postProcess(new StandardReactiveWebEnvironment());
assertThat(output).contains(WEB_LOGGING_HINT);
}
@Test
void postProcessWhenNonWebEnvironmentDoesNotLogWebLoggingHint(CapturedOutput output) throws Exception {
postProcess(new StandardEnvironment());
assertThat(output).doesNotContain(WEB_LOGGING_HINT);
}
@Test
void postProcessWhenWebLoggingIsConfiguredDoesNotLogWebLoggingHint(CapturedOutput output) throws Exception {
StandardReactiveWebEnvironment environment = new StandardReactiveWebEnvironment();
TestPropertyValues.of("logging.level.web=DEBUG").applyTo(environment);
postProcess(environment);
assertThat(output).doesNotContain(WEB_LOGGING_HINT);
}
private void postProcess(ConfigurableEnvironment environment) throws Exception {
DevToolsPropertyDefaultsPostProcessor postProcessor = new DevToolsPropertyDefaultsPostProcessor();
// Run in a new thread so that DevTools is not disabled by the test runner
Thread thread = new Thread(() -> postProcessor.postProcessEnvironment(environment, new SpringApplication()));
thread.start();
thread.join();
replayDeferredLog();
}
private void replayDeferredLog() {
Object logger = ReflectionTestUtils.getField(DevToolsPropertyDefaultsPostProcessor.class, "logger");
assertThat(logger).isInstanceOf(DeferredLog.class);
((DeferredLog) logger).switchTo(DevToolsPropertyDefaultsPostProcessor.class);
}
}