mirror of
https://github.com/spring-projects/spring-boot.git
synced 2026-09-17 12:09:16 +00:00
Consider modules when deducing WebApplicationType
Introduce a strategy to `WebApplicationType` to allow modules to implement deduction logic. Prior to this commit, modules played no part in deducing the `WebApplicationType`. This meant that a user with `spring-webflux` for client purposes would deduce `REACTIVE` despite no `spring-boot-webflux` module being present. The following deduction logic order is now implemented: 1) If the `spring-boot-webmvc` module is being used and Spring MVC classes are found then `SERVLET` is used. 2) If the `spring-boot-webflux` module is being used and Spring WebFlux classes are found then `REACTIVE` is used. 3) If `spring-web` is found and servlet classes are available then `SERVLET` is used. 4) If none of the above are satisfied, `NONE` is used. This commit also updates `SpringBootTestContextBootstrapper` to use the same deduction logic. Fixes gh-48517
This commit is contained in:
+2
-26
@@ -82,16 +82,6 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public class SpringBootTestContextBootstrapper extends DefaultTestContextBootstrapper {
|
||||
|
||||
private static final String[] WEB_ENVIRONMENT_CLASSES = { "jakarta.servlet.Servlet",
|
||||
"org.springframework.web.context.ConfigurableWebApplicationContext" };
|
||||
|
||||
private static final String REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework."
|
||||
+ "web.reactive.DispatcherHandler";
|
||||
|
||||
private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework.web.servlet.DispatcherServlet";
|
||||
|
||||
private static final String JERSEY_WEB_ENVIRONMENT_CLASS = "org.glassfish.jersey.server.ResourceConfig";
|
||||
|
||||
private static final String ACTIVATE_SERVLET_LISTENER = "org.springframework.test."
|
||||
+ "context.web.ServletTestExecutionListener.activateListener";
|
||||
|
||||
@@ -112,7 +102,7 @@ public class SpringBootTestContextBootstrapper extends DefaultTestContextBootstr
|
||||
TestContext context = super.buildTestContext();
|
||||
verifyConfiguration(context.getTestClass());
|
||||
WebEnvironment webEnvironment = getWebEnvironment(context.getTestClass());
|
||||
if (webEnvironment == WebEnvironment.MOCK && deduceWebApplicationType() == WebApplicationType.SERVLET) {
|
||||
if (webEnvironment == WebEnvironment.MOCK && WebApplicationType.deduce() == WebApplicationType.SERVLET) {
|
||||
context.setAttribute(ACTIVATE_SERVLET_LISTENER, true);
|
||||
}
|
||||
else if (webEnvironment != null && webEnvironment.isEmbedded()) {
|
||||
@@ -171,21 +161,7 @@ public class SpringBootTestContextBootstrapper extends DefaultTestContextBootstr
|
||||
TestPropertySourceUtils.convertInlinedPropertiesToMap(configuration.getPropertySourceProperties()));
|
||||
Binder binder = new Binder(source);
|
||||
return binder.bind("spring.main.web-application-type", Bindable.of(WebApplicationType.class))
|
||||
.orElseGet(this::deduceWebApplicationType);
|
||||
}
|
||||
|
||||
private WebApplicationType deduceWebApplicationType() {
|
||||
if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
|
||||
&& !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)
|
||||
&& !ClassUtils.isPresent(JERSEY_WEB_ENVIRONMENT_CLASS, null)) {
|
||||
return WebApplicationType.REACTIVE;
|
||||
}
|
||||
for (String className : WEB_ENVIRONMENT_CLASSES) {
|
||||
if (!ClassUtils.isPresent(className, null)) {
|
||||
return WebApplicationType.NONE;
|
||||
}
|
||||
}
|
||||
return WebApplicationType.SERVLET;
|
||||
.orElseGet(WebApplicationType::deduce);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -275,7 +275,7 @@ public class SpringApplication {
|
||||
this.resourceLoader = resourceLoader;
|
||||
Assert.notNull(primarySources, "'primarySources' must not be null");
|
||||
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
|
||||
this.properties.setWebApplicationType(WebApplicationType.deduceFromClasspath());
|
||||
this.properties.setWebApplicationType(WebApplicationType.deduce());
|
||||
this.bootstrapRegistryInitializers = new ArrayList<>(
|
||||
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
|
||||
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.jspecify.annotations.Nullable;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
@@ -28,6 +29,7 @@ import org.springframework.util.ClassUtils;
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Brian Clozel
|
||||
* @author Phillip Webb
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public enum WebApplicationType {
|
||||
@@ -53,23 +55,28 @@ public enum WebApplicationType {
|
||||
private static final String[] SERVLET_INDICATOR_CLASSES = { "jakarta.servlet.Servlet",
|
||||
"org.springframework.web.context.ConfigurableWebApplicationContext" };
|
||||
|
||||
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
|
||||
|
||||
private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
|
||||
|
||||
private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
|
||||
|
||||
static WebApplicationType deduceFromClasspath() {
|
||||
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
|
||||
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
|
||||
return WebApplicationType.REACTIVE;
|
||||
}
|
||||
for (String className : SERVLET_INDICATOR_CLASSES) {
|
||||
if (!ClassUtils.isPresent(className, null)) {
|
||||
return WebApplicationType.NONE;
|
||||
/**
|
||||
* Deduce the {@link WebApplicationType} from the current classpath.
|
||||
* @return the deduced web application
|
||||
* @since 4.0.1
|
||||
*/
|
||||
public static WebApplicationType deduce() {
|
||||
for (Deducer deducer : SpringFactoriesLoader.forDefaultResourceLocation().load(Deducer.class)) {
|
||||
WebApplicationType deduced = deducer.deduceWebApplicationType();
|
||||
if (deduced != null) {
|
||||
return deduced;
|
||||
}
|
||||
}
|
||||
return WebApplicationType.SERVLET;
|
||||
return isServletApplication() ? WebApplicationType.SERVLET : WebApplicationType.NONE;
|
||||
}
|
||||
|
||||
private static boolean isServletApplication() {
|
||||
for (String servletIndicatorClass : SERVLET_INDICATOR_CLASSES) {
|
||||
if (!ClassUtils.isPresent(servletIndicatorClass, null)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static class WebApplicationTypeRuntimeHints implements RuntimeHintsRegistrar {
|
||||
@@ -79,9 +86,6 @@ public enum WebApplicationType {
|
||||
for (String servletIndicatorClass : SERVLET_INDICATOR_CLASSES) {
|
||||
registerTypeIfPresent(servletIndicatorClass, classLoader, hints);
|
||||
}
|
||||
registerTypeIfPresent(JERSEY_INDICATOR_CLASS, classLoader, hints);
|
||||
registerTypeIfPresent(WEBFLUX_INDICATOR_CLASS, classLoader, hints);
|
||||
registerTypeIfPresent(WEBMVC_INDICATOR_CLASS, classLoader, hints);
|
||||
}
|
||||
|
||||
private void registerTypeIfPresent(String typeName, @Nullable ClassLoader classLoader, RuntimeHints hints) {
|
||||
@@ -92,4 +96,21 @@ public enum WebApplicationType {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Strategy that may be implemented by a module that can deduce the
|
||||
* {@link WebApplicationType}.
|
||||
*
|
||||
* @since 4.0.1
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Deducer {
|
||||
|
||||
/**
|
||||
* Deduce the web application type.
|
||||
* @return the deduced web application type or {@code null}
|
||||
*/
|
||||
@Nullable WebApplicationType deduceWebApplicationType();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.webflux;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link WebApplicationType} deducer to ensure Spring WebFlux applications use
|
||||
* {@link WebApplicationType#REACTIVE}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@Order(20) // Ordered after MVC
|
||||
class WebFluxWebApplicationTypeDeducer implements WebApplicationType.Deducer {
|
||||
|
||||
private static final String[] INDICATOR_CLASSES = { "reactor.core.publisher.Mono",
|
||||
"org.springframework.web.reactive.DispatcherHandler" };
|
||||
|
||||
@Override
|
||||
public @Nullable WebApplicationType deduceWebApplicationType() {
|
||||
// Guard in case the classic module is being used and dependencies are excluded
|
||||
for (String indicatorClass : INDICATOR_CLASSES) {
|
||||
if (!ClassUtils.isPresent(indicatorClass, null)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return WebApplicationType.REACTIVE;
|
||||
}
|
||||
|
||||
static class WebFluxWebApplicationTypeDeducerRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
|
||||
for (String servletIndicatorClass : INDICATOR_CLASSES) {
|
||||
registerTypeIfPresent(servletIndicatorClass, classLoader, hints);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerTypeIfPresent(String typeName, @Nullable ClassLoader classLoader, RuntimeHints hints) {
|
||||
if (ClassUtils.isPresent(typeName, classLoader)) {
|
||||
hints.reflection().registerType(TypeReference.of(typeName));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core integration between Spring Boot and Spring WebFlux.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.boot.webflux;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
@@ -0,0 +1,3 @@
|
||||
# WebApplicationType Deducers
|
||||
org.springframework.boot.WebApplicationType$Deducer=\
|
||||
org.springframework.boot.webflux.WebFluxWebApplicationTypeDeducer
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.aot.hint.RuntimeHintsRegistrar=\
|
||||
org.springframework.boot.webflux.WebFluxWebApplicationTypeDeducer$WebFluxWebApplicationTypeDeducerRuntimeHints
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.webmvc;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link WebApplicationType} deducer to ensure Spring MVC applications use
|
||||
* {@link WebApplicationType#SERVLET}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@Order(10) // Ordered after WebFlux
|
||||
class WebMvcWebApplicationTypeDeducer implements WebApplicationType.Deducer {
|
||||
|
||||
private static final String[] INDICATOR_CLASSES = { "jakarta.servlet.Servlet",
|
||||
"org.springframework.web.servlet.DispatcherServlet",
|
||||
"org.springframework.web.context.ConfigurableWebApplicationContext" };
|
||||
|
||||
@Override
|
||||
public @Nullable WebApplicationType deduceWebApplicationType() {
|
||||
// Guard in case the classic module is being used and dependencies are excluded
|
||||
for (String indicatorClass : INDICATOR_CLASSES) {
|
||||
if (!ClassUtils.isPresent(indicatorClass, null)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return WebApplicationType.SERVLET;
|
||||
}
|
||||
|
||||
static class WebMvcWebApplicationTypeDeducerRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
|
||||
for (String servletIndicatorClass : INDICATOR_CLASSES) {
|
||||
registerTypeIfPresent(servletIndicatorClass, classLoader, hints);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerTypeIfPresent(String typeName, @Nullable ClassLoader classLoader, RuntimeHints hints) {
|
||||
if (ClassUtils.isPresent(typeName, classLoader)) {
|
||||
hints.reflection().registerType(TypeReference.of(typeName));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core integration between Spring Boot and Spring Web MVC.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.boot.webmvc;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
@@ -1,3 +1,7 @@
|
||||
# Template Availability Providers
|
||||
org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\
|
||||
org.springframework.boot.webmvc.autoconfigure.JspTemplateAvailabilityProvider
|
||||
|
||||
# WebApplicationType Deducers
|
||||
org.springframework.boot.WebApplicationType$Deducer=\
|
||||
org.springframework.boot.webmvc.WebMvcWebApplicationTypeDeducer
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.aot.hint.RuntimeHintsRegistrar=\
|
||||
org.springframework.boot.webmvc.WebMvcWebApplicationTypeDeducer$WebMvcWebApplicationTypeDeducerRuntimeHints
|
||||
@@ -25,6 +25,7 @@ dependencies {
|
||||
implementation(project(":starter:spring-boot-starter-webflux"))
|
||||
|
||||
testImplementation(project(":starter:spring-boot-starter-test"))
|
||||
testImplementation(project(":test-support:spring-boot-test-support"))
|
||||
}
|
||||
|
||||
tasks.named("compileTestJava") {
|
||||
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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 smoketest.webapplicationtype;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.testsupport.classpath.ClassPathExclusions;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link WebApplicationType} deducers.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class WebApplicationTypeIntegrationTests {
|
||||
|
||||
@Test
|
||||
@ClassPathExclusions("spring-boot-webflux*")
|
||||
void deduceWhenNoWebFluxModuleAndWebMvcModule() {
|
||||
assertThat(WebApplicationType.deduce()).isEqualTo(WebApplicationType.SERVLET);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ClassPathExclusions({ "spring-boot-webflux*", "spring-web-*" })
|
||||
void deduceWhenNoWebFluxModuleAndNoSpringWebAndWebMvcModule() {
|
||||
assertThat(WebApplicationType.deduce()).isEqualTo(WebApplicationType.NONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ClassPathExclusions("spring-boot-webmvc*")
|
||||
void deduceWhenNoWebMvcModuleAndWebFluxModule() {
|
||||
assertThat(WebApplicationType.deduce()).isEqualTo(WebApplicationType.REACTIVE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ClassPathExclusions({ "spring-boot-webmvc*", "spring-webflux-*" })
|
||||
void deduceWhenNoWebMvcModuleAndNoWebFluxAndWebFluxModule() {
|
||||
assertThat(WebApplicationType.deduce()).isEqualTo(WebApplicationType.SERVLET);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ClassPathExclusions({ "spring-boot-webmvc*", "spring-webflux-*", "spring-web-*" })
|
||||
void deduceWhenNoWebMvcModuleAndNoWebFluxAndNoWebAndWebFluxModule() {
|
||||
assertThat(WebApplicationType.deduce()).isEqualTo(WebApplicationType.NONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void deduceWhenWebMvcAndWebFlux() {
|
||||
assertThat(WebApplicationType.deduce()).isEqualTo(WebApplicationType.SERVLET);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ClassPathExclusions("spring-webmvc-*")
|
||||
void deduceWhenNoWebMvc() {
|
||||
assertThat(WebApplicationType.deduce()).isEqualTo(WebApplicationType.REACTIVE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ClassPathExclusions({ "spring-webmvc-*", "spring-webflux-*" })
|
||||
void deduceWhenNoWebMvcAndNoWebFlux() {
|
||||
assertThat(WebApplicationType.deduce()).isEqualTo(WebApplicationType.SERVLET);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ClassPathExclusions({ "spring-web-*" })
|
||||
void deduceWhenNoWeb() {
|
||||
assertThat(WebApplicationType.deduce()).isEqualTo(WebApplicationType.NONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ClassPathExclusions(packages = "jakarta.servlet", files = "spring-webflux-*")
|
||||
void deduceWhenNoServletOrWebFlux() {
|
||||
assertThat(WebApplicationType.deduce()).isEqualTo(WebApplicationType.NONE);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user