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:
Phillip Webb
2025-12-15 20:30:16 -08:00
parent f9fa1346d1
commit da516741b2
13 changed files with 329 additions and 45 deletions
@@ -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));
}
}
}
}
@@ -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