From 3241d9cb446565317cf036c0ba19b965c145e0ef Mon Sep 17 00:00:00 2001
From: SavitarC <71271200+savitarc@users.noreply.github.com>
Date: Tue, 12 May 2026 18:46:24 +0800
Subject: [PATCH 1/8] feat(spring-boot): Add Spring Boot 4 support
---
.artifacts | 2 +
dubbo-distribution/dubbo-bom/pom.xml | 10 +
.../autoconfigure/SpringBoot3Condition.java | 2 +-
.../dubbo-spring-boot-4-autoconfigure/pom.xml | 141 ++++++++++++++
.../DubboTriple4AutoConfiguration.java | 104 ++++++++++
.../zipkin/DubboZipkin4AutoConfiguration.java | 77 ++++++++
.../zipkin/Zipkin4Configurations.java | 182 ++++++++++++++++++
.../zipkin/Zipkin4RestTemplateSender.java | 74 +++++++
.../Zipkin4RestTemplateBuilderCustomizer.java | 37 ++++
.../main/resources/META-INF/spring.factories | 3 +
...ot.autoconfigure.AutoConfiguration.imports | 2 +
...Boot4DependencyCheckAutoConfiguration.java | 67 +++++++
.../autoconfigure/SpringBoot3Condition.java | 2 +-
.../autoconfigure/SpringBoot4Condition.java | 32 +++
.../SpringBootBefore4Condition.java | 32 +++
.../zipkin/ZipkinAutoConfiguration.java | 10 +-
...ot.autoconfigure.AutoConfiguration.imports | 1 +
.../dubbo-spring-boot-4-starter/pom.xml | 63 ++++++
.../dubbo-spring-boot-starters/pom.xml | 12 ++
dubbo-test/dubbo-dependencies-all/pom.xml | 10 +
.../org/apache/dubbo/dependency/FileTest.java | 2 +
pom.xml | 3 +
22 files changed, 863 insertions(+), 5 deletions(-)
create mode 100644 dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/pom.xml
create mode 100644 dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTriple4AutoConfiguration.java
create mode 100644 dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/DubboZipkin4AutoConfiguration.java
create mode 100644 dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/Zipkin4Configurations.java
create mode 100644 dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/Zipkin4RestTemplateSender.java
create mode 100644 dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/customizer/Zipkin4RestTemplateBuilderCustomizer.java
create mode 100644 dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/resources/META-INF/spring.factories
create mode 100644 dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
create mode 100644 dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboSpringBoot4DependencyCheckAutoConfiguration.java
create mode 100644 dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBoot4Condition.java
create mode 100644 dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBootBefore4Condition.java
create mode 100644 dubbo-spring-boot-project/dubbo-spring-boot-starters/dubbo-spring-boot-4-starter/pom.xml
diff --git a/.artifacts b/.artifacts
index 28b0249adb27..1e02cf91f3cb 100644
--- a/.artifacts
+++ b/.artifacts
@@ -95,6 +95,7 @@ dubbo-spring-boot-actuator-autoconfigure
dubbo-spring-boot-actuator-compatible
dubbo-spring-boot-autoconfigure
dubbo-spring-boot-3-autoconfigure
+dubbo-spring-boot-4-autoconfigure
dubbo-spring-boot-autoconfigure-compatible
dubbo-spring-boot-actuator-autoconfigure-compatible
dubbo-spring-boot-compatible
@@ -103,6 +104,7 @@ dubbo-tracing-otel-zipkin-spring-boot-starter
dubbo-tracing-otel-otlp-spring-boot-starter
dubbo-observability-spring-boot-starter
dubbo-spring-boot-starter
+dubbo-spring-boot-4-starter
dubbo-spring-boot-starters
dubbo-spring-boot-interceptor
dubbo-nacos-spring-boot-starter
diff --git a/dubbo-distribution/dubbo-bom/pom.xml b/dubbo-distribution/dubbo-bom/pom.xml
index d6994783575e..3c3225ddc518 100644
--- a/dubbo-distribution/dubbo-bom/pom.xml
+++ b/dubbo-distribution/dubbo-bom/pom.xml
@@ -498,6 +498,11 @@
dubbo-spring-boot-3-autoconfigure
${project.version}
+
+ org.apache.dubbo
+ dubbo-spring-boot-4-autoconfigure
+ ${project.version}
+
org.apache.dubbo
dubbo-spring-boot-compatible
@@ -519,6 +524,11 @@
dubbo-spring-boot-starter
${project.version}
+
+ org.apache.dubbo
+ dubbo-spring-boot-4-starter
+ ${project.version}
+
org.apache.dubbo
dubbo-spring-boot
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-3-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBoot3Condition.java b/dubbo-spring-boot-project/dubbo-spring-boot-3-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBoot3Condition.java
index 489446f1d75c..1bad7b9ba937 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-3-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBoot3Condition.java
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-3-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBoot3Condition.java
@@ -23,7 +23,7 @@
public class SpringBoot3Condition implements Condition {
- public static boolean IS_SPRING_BOOT_3 = SpringBootVersion.getVersion().charAt(0) >= '3';
+ public static boolean IS_SPRING_BOOT_3 = SpringBootVersion.getVersion().charAt(0) == '3';
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/pom.xml b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/pom.xml
new file mode 100644
index 000000000000..7c95584e7f62
--- /dev/null
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/pom.xml
@@ -0,0 +1,141 @@
+
+
+
+ 4.0.0
+
+ org.apache.dubbo
+ dubbo-parent
+ ${revision}
+ ../../pom.xml
+
+
+ dubbo-spring-boot-4-autoconfigure
+ jar
+ Apache Dubbo Spring Boot 4 Auto-Configure
+
+
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${spring-boot-4.version}
+ pom
+ import
+
+
+
+
+
+
+ org.apache.dubbo
+ dubbo-spring-boot-autoconfigure
+ ${project.version}
+
+
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+ true
+
+
+ org.springframework.boot
+ spring-boot-tomcat
+ true
+
+
+ org.springframework.boot
+ spring-boot-web-server
+ true
+
+
+ org.springframework.boot
+ spring-boot-restclient
+ true
+
+
+
+
+ org.apache.dubbo
+ dubbo
+ ${project.version}
+ true
+
+
+ org.apache.dubbo
+ dubbo-triple-servlet
+ ${project.version}
+ true
+
+
+ org.apache.dubbo
+ dubbo-triple-websocket
+ ${project.version}
+ true
+
+
+
+ jakarta.servlet
+ jakarta.servlet-api
+ provided
+
+
+ jakarta.websocket
+ jakarta.websocket-api
+ provided
+
+
+ jakarta.websocket
+ jakarta.websocket-client-api
+ provided
+
+
+
+
+ io.micrometer
+ micrometer-tracing-bridge-brave
+ true
+
+
+ io.opentelemetry
+ opentelemetry-exporter-zipkin
+ true
+
+
+ io.zipkin.reporter2
+ zipkin-reporter-brave
+ true
+
+
+ io.zipkin.reporter2
+ zipkin-sender-urlconnection
+ true
+
+
+
+ org.springframework
+ spring-web
+ true
+
+
+ org.springframework
+ spring-webflux
+ true
+
+
+
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTriple4AutoConfiguration.java b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTriple4AutoConfiguration.java
new file mode 100644
index 000000000000..54dbe31b3f7d
--- /dev/null
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTriple4AutoConfiguration.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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.apache.dubbo.spring.boot.autoconfigure;
+
+import org.apache.dubbo.rpc.protocol.tri.ServletExchanger;
+import org.apache.dubbo.rpc.protocol.tri.servlet.jakarta.TripleFilter;
+import org.apache.dubbo.rpc.protocol.tri.websocket.jakarta.TripleWebSocketFilter;
+
+import jakarta.servlet.Filter;
+import org.apache.coyote.ProtocolHandler;
+import org.apache.coyote.UpgradeProtocol;
+import org.apache.coyote.http2.Http2Protocol;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
+import org.springframework.boot.tomcat.ConfigurableTomcatWebServerFactory;
+import org.springframework.boot.web.server.WebServerFactoryCustomizer;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Conditional;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration(proxyBeanMethods = false)
+@Conditional(SpringBoot4Condition.class)
+public class DubboTriple4AutoConfiguration {
+
+ public static final String SERVLET_PREFIX = "dubbo.protocol.triple.servlet";
+
+ public static final String WEBSOCKET_PREFIX = "dubbo.protocol.triple.websocket";
+
+ @Configuration(proxyBeanMethods = false)
+ @ConditionalOnClass(Filter.class)
+ @ConditionalOnWebApplication(type = Type.SERVLET)
+ @ConditionalOnProperty(prefix = SERVLET_PREFIX, name = "enabled", havingValue = "true")
+ public static class TripleServletConfiguration {
+
+ @Bean
+ public FilterRegistrationBean tripleProtocolFilter(
+ @Value("${" + SERVLET_PREFIX + ".filter-url-patterns:/*}") String[] urlPatterns,
+ @Value("${" + SERVLET_PREFIX + ".filter-order:-1000000}") int order,
+ @Value("${server.port:8080}") int serverPort) {
+ ServletExchanger.bindServerPort(serverPort);
+ FilterRegistrationBean registrationBean = new FilterRegistrationBean<>();
+ registrationBean.setFilter(new TripleFilter());
+ registrationBean.addUrlPatterns(urlPatterns);
+ registrationBean.setOrder(order);
+ return registrationBean;
+ }
+
+ @Bean
+ @ConditionalOnClass(Http2Protocol.class)
+ @ConditionalOnProperty(prefix = SERVLET_PREFIX, name = "max-concurrent-streams")
+ public WebServerFactoryCustomizer tripleTomcatHttp2Customizer(
+ @Value("${" + SERVLET_PREFIX + ".max-concurrent-streams}") int maxConcurrentStreams) {
+ return factory -> factory.addConnectorCustomizers(connector -> {
+ ProtocolHandler handler = connector.getProtocolHandler();
+ for (UpgradeProtocol upgradeProtocol : handler.findUpgradeProtocols()) {
+ if (upgradeProtocol instanceof Http2Protocol) {
+ Http2Protocol protocol = (Http2Protocol) upgradeProtocol;
+ int value = maxConcurrentStreams <= 0 ? Integer.MAX_VALUE : maxConcurrentStreams;
+ protocol.setMaxConcurrentStreams(value);
+ protocol.setMaxConcurrentStreamExecution(value);
+ }
+ }
+ });
+ }
+ }
+
+ @Configuration(proxyBeanMethods = false)
+ @ConditionalOnClass(Filter.class)
+ @ConditionalOnWebApplication(type = Type.SERVLET)
+ @ConditionalOnProperty(prefix = WEBSOCKET_PREFIX, name = "enabled", havingValue = "true")
+ public static class TripleWebSocketConfiguration {
+
+ @Bean
+ public FilterRegistrationBean tripleWebSocketFilter(
+ @Value("${" + WEBSOCKET_PREFIX + ".filter-url-patterns:/*}") String[] urlPatterns,
+ @Value("${" + WEBSOCKET_PREFIX + ".filter-order:-1000000}") int order,
+ @Value("${server.port:8080}") int serverPort) {
+ ServletExchanger.bindServerPort(serverPort);
+ FilterRegistrationBean registrationBean = new FilterRegistrationBean<>();
+ registrationBean.setFilter(new TripleWebSocketFilter());
+ registrationBean.addUrlPatterns(urlPatterns);
+ registrationBean.setOrder(order);
+ return registrationBean;
+ }
+ }
+}
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/DubboZipkin4AutoConfiguration.java b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/DubboZipkin4AutoConfiguration.java
new file mode 100644
index 000000000000..c38ff34461a3
--- /dev/null
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/DubboZipkin4AutoConfiguration.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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.apache.dubbo.spring.boot.autoconfigure.observability.zipkin;
+
+import org.apache.dubbo.spring.boot.autoconfigure.SpringBoot4Condition;
+import org.apache.dubbo.spring.boot.autoconfigure.observability.annotation.ConditionalOnDubboTracingEnable;
+import org.apache.dubbo.spring.boot.autoconfigure.observability.zipkin.Zipkin4Configurations.BraveConfiguration;
+import org.apache.dubbo.spring.boot.autoconfigure.observability.zipkin.Zipkin4Configurations.OpenTelemetryConfiguration;
+import org.apache.dubbo.spring.boot.autoconfigure.observability.zipkin.Zipkin4Configurations.ReporterConfiguration;
+import org.apache.dubbo.spring.boot.autoconfigure.observability.zipkin.Zipkin4Configurations.SenderConfiguration;
+
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Conditional;
+import org.springframework.context.annotation.Import;
+import zipkin2.Span;
+import zipkin2.codec.BytesEncoder;
+import zipkin2.codec.SpanBytesEncoder;
+import zipkin2.reporter.Sender;
+
+import static org.apache.dubbo.spring.boot.autoconfigure.observability.ObservabilityUtils.DUBBO_TRACING_ZIPKIN_CONFIG_PREFIX;
+import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_PREFIX;
+
+/**
+ * {@link EnableAutoConfiguration Auto-configuration} for Zipkin on Spring Boot 4.
+ *
+ * @since 3.3.7
+ */
+@ConditionalOnProperty(prefix = DUBBO_PREFIX, name = "enabled", matchIfMissing = true)
+@AutoConfiguration(
+ afterName = {
+ "org.springframework.boot.restclient.autoconfigure.RestTemplateAutoConfiguration",
+ "org.springframework.boot.actuate.autoconfigure.tracing.zipkin"
+ })
+@ConditionalOnClass(Sender.class)
+@Import({
+ SenderConfiguration.class,
+ ReporterConfiguration.class,
+ BraveConfiguration.class,
+ OpenTelemetryConfiguration.class
+})
+@ConditionalOnDubboTracingEnable
+@Conditional(SpringBoot4Condition.class)
+public class DubboZipkin4AutoConfiguration {
+
+ @Bean
+ @ConditionalOnProperty(prefix = DUBBO_TRACING_ZIPKIN_CONFIG_PREFIX, name = "endpoint")
+ @ConditionalOnMissingBean
+ public BytesEncoder spanBytesEncoder() {
+ return SpanBytesEncoder.JSON_V2;
+ }
+
+ @Bean
+ @ConditionalOnProperty(prefix = DUBBO_TRACING_ZIPKIN_CONFIG_PREFIX, name = "endpoint")
+ @ConditionalOnMissingBean
+ public zipkin2.reporter.BytesEncoder reporterBytesEncoder() {
+ return zipkin2.reporter.SpanBytesEncoder.JSON_V2;
+ }
+}
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/Zipkin4Configurations.java b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/Zipkin4Configurations.java
new file mode 100644
index 000000000000..f48d84618df0
--- /dev/null
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/Zipkin4Configurations.java
@@ -0,0 +1,182 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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.apache.dubbo.spring.boot.autoconfigure.observability.zipkin;
+
+import org.apache.dubbo.config.nested.ExporterConfig;
+import org.apache.dubbo.spring.boot.autoconfigure.DubboConfigurationProperties;
+import org.apache.dubbo.spring.boot.autoconfigure.observability.zipkin.customizer.Zipkin4RestTemplateBuilderCustomizer;
+import org.apache.dubbo.spring.boot.autoconfigure.observability.zipkin.customizer.ZipkinWebClientBuilderCustomizer;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import io.opentelemetry.exporter.zipkin.ZipkinSpanExporter;
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.restclient.RestTemplateBuilder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.web.reactive.function.client.WebClient;
+import zipkin2.Span;
+import zipkin2.codec.BytesEncoder;
+import zipkin2.reporter.AsyncReporter;
+import zipkin2.reporter.Reporter;
+import zipkin2.reporter.Sender;
+import zipkin2.reporter.brave.ZipkinSpanHandler;
+import zipkin2.reporter.urlconnection.URLConnectionSender;
+
+import static org.apache.dubbo.spring.boot.autoconfigure.observability.ObservabilityUtils.DUBBO_TRACING_ZIPKIN_CONFIG_PREFIX;
+
+/**
+ * Configurations for Zipkin on Spring Boot 4. Those are imported by {@link DubboZipkin4AutoConfiguration}.
+ */
+class Zipkin4Configurations {
+
+ @Configuration(proxyBeanMethods = false)
+ @ConditionalOnProperty(prefix = DUBBO_TRACING_ZIPKIN_CONFIG_PREFIX, name = "endpoint")
+ @Import({
+ UrlConnectionSenderConfiguration.class,
+ WebClientSenderConfiguration.class,
+ RestTemplateSenderConfiguration.class
+ })
+ static class SenderConfiguration {}
+
+ @Configuration(proxyBeanMethods = false)
+ @ConditionalOnClass(URLConnectionSender.class)
+ @EnableConfigurationProperties(DubboConfigurationProperties.class)
+ static class UrlConnectionSenderConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean(Sender.class)
+ URLConnectionSender urlConnectionSender(DubboConfigurationProperties properties) {
+ URLConnectionSender.Builder builder = URLConnectionSender.newBuilder();
+ ExporterConfig.ZipkinConfig zipkinConfig =
+ properties.getTracing().getTracingExporter().getZipkinConfig();
+ builder.connectTimeout((int) zipkinConfig.getConnectTimeout().toMillis());
+ builder.readTimeout((int) zipkinConfig.getReadTimeout().toMillis());
+ builder.endpoint(zipkinConfig.getEndpoint());
+ return builder.build();
+ }
+ }
+
+ @Configuration(proxyBeanMethods = false)
+ @ConditionalOnClass(RestTemplate.class)
+ @EnableConfigurationProperties(DubboConfigurationProperties.class)
+ static class RestTemplateSenderConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean(Sender.class)
+ Zipkin4RestTemplateSender restTemplateSender(
+ DubboConfigurationProperties properties,
+ ObjectProvider customizers) {
+ ExporterConfig.ZipkinConfig zipkinConfig =
+ properties.getTracing().getTracingExporter().getZipkinConfig();
+ RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder()
+ .connectTimeout(zipkinConfig.getConnectTimeout())
+ .readTimeout(zipkinConfig.getReadTimeout());
+ restTemplateBuilder = applyCustomizers(restTemplateBuilder, customizers);
+ return new Zipkin4RestTemplateSender(zipkinConfig.getEndpoint(), restTemplateBuilder.build());
+ }
+
+ private RestTemplateBuilder applyCustomizers(
+ RestTemplateBuilder restTemplateBuilder,
+ ObjectProvider customizers) {
+ Iterable orderedCustomizers =
+ () -> customizers.orderedStream().iterator();
+ RestTemplateBuilder currentBuilder = restTemplateBuilder;
+ for (Zipkin4RestTemplateBuilderCustomizer customizer : orderedCustomizers) {
+ currentBuilder = customizer.customize(currentBuilder);
+ }
+ return currentBuilder;
+ }
+ }
+
+ @Configuration(proxyBeanMethods = false)
+ @ConditionalOnClass(WebClient.class)
+ @EnableConfigurationProperties(DubboConfigurationProperties.class)
+ static class WebClientSenderConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean(Sender.class)
+ ZipkinWebClientSender webClientSender(
+ DubboConfigurationProperties properties, ObjectProvider customizers) {
+ ExporterConfig.ZipkinConfig zipkinConfig =
+ properties.getTracing().getTracingExporter().getZipkinConfig();
+ WebClient.Builder builder = WebClient.builder();
+ customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
+ return new ZipkinWebClientSender(zipkinConfig.getEndpoint(), builder.build());
+ }
+ }
+
+ @Configuration(proxyBeanMethods = false)
+ static class ReporterConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean
+ @ConditionalOnBean(Sender.class)
+ AsyncReporter spanReporter(Sender sender, zipkin2.reporter.BytesEncoder encoder) {
+ return AsyncReporter.builder(sender).build(encoder);
+ }
+ }
+
+ @Configuration(proxyBeanMethods = false)
+ @ConditionalOnClass(ZipkinSpanHandler.class)
+ static class BraveConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean
+ @ConditionalOnBean(Reporter.class)
+ ZipkinSpanHandler zipkinSpanHandler(Reporter spanReporter) {
+ return (ZipkinSpanHandler)
+ ZipkinSpanHandler.newBuilder(spanReporter).build();
+ }
+ }
+
+ @Configuration(proxyBeanMethods = false)
+ @ConditionalOnClass(ZipkinSpanExporter.class)
+ @ConditionalOnProperty(prefix = DUBBO_TRACING_ZIPKIN_CONFIG_PREFIX, name = "endpoint")
+ @EnableConfigurationProperties(DubboConfigurationProperties.class)
+ static class OpenTelemetryConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean
+ ZipkinSpanExporter zipkinSpanExporter(
+ DubboConfigurationProperties properties, BytesEncoder encoder, ObjectProvider senders) {
+ AtomicReference senderRef = new AtomicReference<>();
+ senders.orderedStream().findFirst().ifPresent(senderRef::set);
+ Sender sender = senderRef.get();
+ if (sender == null) {
+ ExporterConfig.ZipkinConfig zipkinConfig =
+ properties.getTracing().getTracingExporter().getZipkinConfig();
+ return ZipkinSpanExporter.builder()
+ .setEncoder(encoder)
+ .setEndpoint(zipkinConfig.getEndpoint())
+ .setReadTimeout(zipkinConfig.getReadTimeout())
+ .build();
+ }
+ return ZipkinSpanExporter.builder()
+ .setEncoder(encoder)
+ .setSender(sender)
+ .build();
+ }
+ }
+}
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/Zipkin4RestTemplateSender.java b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/Zipkin4RestTemplateSender.java
new file mode 100644
index 000000000000..2eef3692ed14
--- /dev/null
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/Zipkin4RestTemplateSender.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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.apache.dubbo.spring.boot.autoconfigure.observability.zipkin;
+
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpMethod;
+import org.springframework.web.client.RestTemplate;
+import zipkin2.reporter.Call;
+import zipkin2.reporter.Callback;
+
+class Zipkin4RestTemplateSender extends HttpSender {
+ private final String endpoint;
+
+ private final RestTemplate restTemplate;
+
+ Zipkin4RestTemplateSender(String endpoint, RestTemplate restTemplate) {
+ this.endpoint = endpoint;
+ this.restTemplate = restTemplate;
+ }
+
+ @Override
+ public HttpPostCall sendSpans(byte[] batchedEncodedSpans) {
+ return new RestTemplateHttpPostCall(this.endpoint, batchedEncodedSpans, this.restTemplate);
+ }
+
+ private static class RestTemplateHttpPostCall extends HttpPostCall {
+
+ private final String endpoint;
+
+ private final RestTemplate restTemplate;
+
+ RestTemplateHttpPostCall(String endpoint, byte[] body, RestTemplate restTemplate) {
+ super(body);
+ this.endpoint = endpoint;
+ this.restTemplate = restTemplate;
+ }
+
+ @Override
+ public Call clone() {
+ return new RestTemplateHttpPostCall(this.endpoint, getUncompressedBody(), this.restTemplate);
+ }
+
+ @Override
+ protected Void doExecute() {
+ HttpEntity request = new HttpEntity<>(getBody(), getDefaultHeaders());
+ this.restTemplate.exchange(this.endpoint, HttpMethod.POST, request, Void.class);
+ return null;
+ }
+
+ @Override
+ protected void doEnqueue(Callback callback) {
+ try {
+ doExecute();
+ callback.onSuccess(null);
+ } catch (Exception ex) {
+ callback.onError(ex);
+ }
+ }
+ }
+}
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/customizer/Zipkin4RestTemplateBuilderCustomizer.java b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/customizer/Zipkin4RestTemplateBuilderCustomizer.java
new file mode 100644
index 000000000000..372b3c35dcf4
--- /dev/null
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/customizer/Zipkin4RestTemplateBuilderCustomizer.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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.apache.dubbo.spring.boot.autoconfigure.observability.zipkin.customizer;
+
+import org.springframework.boot.restclient.RestTemplateBuilder;
+
+/**
+ * Callback interface that can be implemented by beans wishing to customize the
+ * {@link RestTemplateBuilder} used to send spans to Zipkin on Spring Boot 4.
+ *
+ * @since 3.3.7
+ */
+@FunctionalInterface
+public interface Zipkin4RestTemplateBuilderCustomizer {
+
+ /**
+ * Customize the rest template builder.
+ *
+ * @param restTemplateBuilder the {@code RestTemplateBuilder} to customize
+ * @return the customized {@code RestTemplateBuilder}
+ */
+ RestTemplateBuilder customize(RestTemplateBuilder restTemplateBuilder);
+}
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/resources/META-INF/spring.factories b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/resources/META-INF/spring.factories
new file mode 100644
index 000000000000..9befabcd3f35
--- /dev/null
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,3 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+org.apache.dubbo.spring.boot.autoconfigure.DubboTriple4AutoConfiguration,\
+ org.apache.dubbo.spring.boot.autoconfigure.observability.zipkin.DubboZipkin4AutoConfiguration
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 000000000000..b27b1650b5f6
--- /dev/null
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1,2 @@
+org.apache.dubbo.spring.boot.autoconfigure.DubboTriple4AutoConfiguration
+org.apache.dubbo.spring.boot.autoconfigure.observability.zipkin.DubboZipkin4AutoConfiguration
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboSpringBoot4DependencyCheckAutoConfiguration.java b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboSpringBoot4DependencyCheckAutoConfiguration.java
new file mode 100644
index 000000000000..cbc6b00886b0
--- /dev/null
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboSpringBoot4DependencyCheckAutoConfiguration.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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.apache.dubbo.spring.boot.autoconfigure;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Conditional;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration(proxyBeanMethods = false)
+@Conditional(SpringBoot4Condition.class)
+public class DubboSpringBoot4DependencyCheckAutoConfiguration {
+
+ public static final String SERVLET_PREFIX = "dubbo.protocol.triple.servlet";
+
+ public static final String WEBSOCKET_PREFIX = "dubbo.protocol.triple.websocket";
+
+ public static final String JAKARATA_SERVLET_FILTER = "jakarta.servlet.Filter";
+
+ public static final String DUBBO_TRIPLE_4_AUTOCONFIGURATION =
+ "org.apache.dubbo.spring.boot.autoconfigure.DubboTriple4AutoConfiguration";
+
+ private static final String SPRING_BOOT_4_DEPENDENCY_CHECK_WARNING =
+ "Couldn't enable servlet support for triple at SpringBoot4: Missing dubbo-spring-boot-4-autoconfigure";
+
+ @Configuration(proxyBeanMethods = false)
+ @ConditionalOnClass(name = JAKARATA_SERVLET_FILTER)
+ @ConditionalOnWebApplication(type = Type.SERVLET)
+ @ConditionalOnProperty(prefix = SERVLET_PREFIX, name = "enabled", havingValue = "true")
+ @ConditionalOnMissingClass(DUBBO_TRIPLE_4_AUTOCONFIGURATION)
+ public static class tripleProtocolFilterDependencyCheckConfiguration {
+ @Bean
+ public Object tripleProtocolFilterDependencyCheck() {
+ throw new IllegalStateException(SPRING_BOOT_4_DEPENDENCY_CHECK_WARNING);
+ }
+ }
+
+ @Configuration(proxyBeanMethods = false)
+ @ConditionalOnClass(name = JAKARATA_SERVLET_FILTER)
+ @ConditionalOnWebApplication(type = Type.SERVLET)
+ @ConditionalOnProperty(prefix = WEBSOCKET_PREFIX, name = "enabled", havingValue = "true")
+ @ConditionalOnMissingClass(DUBBO_TRIPLE_4_AUTOCONFIGURATION)
+ public static class tripleWebSocketFilterDependencyCheckConfiguration {
+ @Bean
+ public Object tripleWebSocketFilterDependencyCheck() {
+ throw new IllegalStateException(SPRING_BOOT_4_DEPENDENCY_CHECK_WARNING);
+ }
+ }
+}
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBoot3Condition.java b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBoot3Condition.java
index 489446f1d75c..1bad7b9ba937 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBoot3Condition.java
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBoot3Condition.java
@@ -23,7 +23,7 @@
public class SpringBoot3Condition implements Condition {
- public static boolean IS_SPRING_BOOT_3 = SpringBootVersion.getVersion().charAt(0) >= '3';
+ public static boolean IS_SPRING_BOOT_3 = SpringBootVersion.getVersion().charAt(0) == '3';
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBoot4Condition.java b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBoot4Condition.java
new file mode 100644
index 000000000000..dd75baba12ac
--- /dev/null
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBoot4Condition.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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.apache.dubbo.spring.boot.autoconfigure;
+
+import org.springframework.boot.SpringBootVersion;
+import org.springframework.context.annotation.Condition;
+import org.springframework.context.annotation.ConditionContext;
+import org.springframework.core.type.AnnotatedTypeMetadata;
+
+public class SpringBoot4Condition implements Condition {
+
+ public static boolean IS_SPRING_BOOT_4 = SpringBootVersion.getVersion().charAt(0) == '4';
+
+ @Override
+ public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
+ return IS_SPRING_BOOT_4;
+ }
+}
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBootBefore4Condition.java b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBootBefore4Condition.java
new file mode 100644
index 000000000000..99505cc750a8
--- /dev/null
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBootBefore4Condition.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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.apache.dubbo.spring.boot.autoconfigure;
+
+import org.springframework.boot.SpringBootVersion;
+import org.springframework.context.annotation.Condition;
+import org.springframework.context.annotation.ConditionContext;
+import org.springframework.core.type.AnnotatedTypeMetadata;
+
+public class SpringBootBefore4Condition implements Condition {
+
+ public static boolean IS_SPRING_BOOT_BEFORE_4 = SpringBootVersion.getVersion().charAt(0) < '4';
+
+ @Override
+ public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
+ return IS_SPRING_BOOT_BEFORE_4;
+ }
+}
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/ZipkinAutoConfiguration.java b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/ZipkinAutoConfiguration.java
index 0b46777b5368..68d2a7b2c807 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/ZipkinAutoConfiguration.java
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/ZipkinAutoConfiguration.java
@@ -16,6 +16,7 @@
*/
package org.apache.dubbo.spring.boot.autoconfigure.observability.zipkin;
+import org.apache.dubbo.spring.boot.autoconfigure.SpringBootBefore4Condition;
import org.apache.dubbo.spring.boot.autoconfigure.observability.annotation.ConditionalOnDubboTracingEnable;
import org.apache.dubbo.spring.boot.autoconfigure.observability.zipkin.ZipkinConfigurations.BraveConfiguration;
import org.apache.dubbo.spring.boot.autoconfigure.observability.zipkin.ZipkinConfigurations.OpenTelemetryConfiguration;
@@ -27,8 +28,8 @@
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration;
import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Import;
import zipkin2.Span;
import zipkin2.codec.BytesEncoder;
@@ -49,8 +50,10 @@
*/
@ConditionalOnProperty(prefix = DUBBO_PREFIX, name = "enabled", matchIfMissing = true)
@AutoConfiguration(
- after = RestTemplateAutoConfiguration.class,
- afterName = "org.springframework.boot.actuate.autoconfigure.tracing.zipkin")
+ afterName = {
+ "org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration",
+ "org.springframework.boot.actuate.autoconfigure.tracing.zipkin"
+ })
@ConditionalOnClass(Sender.class)
@Import({
SenderConfiguration.class,
@@ -59,6 +62,7 @@
OpenTelemetryConfiguration.class
})
@ConditionalOnDubboTracingEnable
+@Conditional(SpringBootBefore4Condition.class)
public class ZipkinAutoConfiguration {
@Bean
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
index c92a1ed528c2..cd23d61b1004 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -9,3 +9,4 @@ org.apache.dubbo.spring.boot.autoconfigure.observability.brave.BraveAutoConfigur
org.apache.dubbo.spring.boot.autoconfigure.observability.zipkin.ZipkinAutoConfiguration
org.apache.dubbo.spring.boot.autoconfigure.observability.otlp.OtlpAutoConfiguration
org.apache.dubbo.spring.boot.autoconfigure.DubboSpringBoot3DependencyCheckAutoConfiguration
+org.apache.dubbo.spring.boot.autoconfigure.DubboSpringBoot4DependencyCheckAutoConfiguration
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-starters/dubbo-spring-boot-4-starter/pom.xml b/dubbo-spring-boot-project/dubbo-spring-boot-starters/dubbo-spring-boot-4-starter/pom.xml
new file mode 100644
index 000000000000..529c60a9fb48
--- /dev/null
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-starters/dubbo-spring-boot-4-starter/pom.xml
@@ -0,0 +1,63 @@
+
+
+
+ 4.0.0
+
+ org.apache.dubbo
+ dubbo-spring-boot-starters
+ ${revision}
+ ../pom.xml
+
+
+ dubbo-spring-boot-4-starter
+ jar
+ Apache Dubbo Spring Boot 4 Starter
+
+
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${spring-boot-4.version}
+ pom
+ import
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+ true
+
+
+
+ org.apache.dubbo
+ dubbo-spring-boot-autoconfigure
+ ${project.version}
+
+
+
+ org.apache.dubbo
+ dubbo-spring-boot-4-autoconfigure
+ ${project.version}
+
+
+
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-starters/pom.xml b/dubbo-spring-boot-project/dubbo-spring-boot-starters/pom.xml
index d3bcc63bcb0c..298a7f16e29c 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-starters/pom.xml
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-starters/pom.xml
@@ -41,4 +41,16 @@
dubbo-observability-spring-boot-starter
+
+
+ jdk-version-ge-17
+
+ [17,)
+
+
+ dubbo-spring-boot-4-starter
+
+
+
+
diff --git a/dubbo-test/dubbo-dependencies-all/pom.xml b/dubbo-test/dubbo-dependencies-all/pom.xml
index 9d5b67d66efb..5557a3671349 100644
--- a/dubbo-test/dubbo-dependencies-all/pom.xml
+++ b/dubbo-test/dubbo-dependencies-all/pom.xml
@@ -499,6 +499,16 @@
dubbo-spring-boot-3-autoconfigure
${project.version}
+
+ org.apache.dubbo
+ dubbo-spring-boot-4-autoconfigure
+ ${project.version}
+
+
+ org.apache.dubbo
+ dubbo-spring-boot-4-starter
+ ${project.version}
+
diff --git a/dubbo-test/dubbo-test-modules/src/test/java/org/apache/dubbo/dependency/FileTest.java b/dubbo-test/dubbo-test-modules/src/test/java/org/apache/dubbo/dependency/FileTest.java
index fdc0d7d95944..44c9b52f4fdf 100644
--- a/dubbo-test/dubbo-test-modules/src/test/java/org/apache/dubbo/dependency/FileTest.java
+++ b/dubbo-test/dubbo-test-modules/src/test/java/org/apache/dubbo/dependency/FileTest.java
@@ -57,6 +57,8 @@ class FileTest {
ignoredModules.add(Pattern.compile("dubbo-config-spring6"));
ignoredModules.add(Pattern.compile("dubbo-spring6-security"));
ignoredModules.add(Pattern.compile("dubbo-spring-boot-3-autoconfigure"));
+ ignoredModules.add(Pattern.compile("dubbo-spring-boot-4-autoconfigure"));
+ ignoredModules.add(Pattern.compile("dubbo-spring-boot-4-starter"));
ignoredModules.add(Pattern.compile("dubbo-plugin-loom.*"));
ignoredModules.add(Pattern.compile("dubbo-mutiny.*"));
ignoredModules.add(Pattern.compile("dubbo-mcp"));
diff --git a/pom.xml b/pom.xml
index 470e44a6c4e6..2363d9d67564 100644
--- a/pom.xml
+++ b/pom.xml
@@ -167,6 +167,7 @@
6.2.18
6.5.10
3.5.14
+ 4.0.6
4.33.4
@@ -498,6 +499,7 @@
dubbo-spring-boot-project/dubbo-spring-boot-3-autoconfigure
+ dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure
dubbo-plugin/dubbo-spring6-security
dubbo-plugin/dubbo-mutiny
@@ -649,6 +651,7 @@
release
dubbo-spring-boot-project/dubbo-spring-boot-3-autoconfigure
+ dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure
dubbo-plugin/dubbo-spring6-security
dubbo-plugin/dubbo-plugin-loom
dubbo-plugin/dubbo-mutiny
From 0d78feca8164a26d6eb8a0767f07ffdabbac151a Mon Sep 17 00:00:00 2001
From: SavitarC <71271200+savitarc@users.noreply.github.com>
Date: Wed, 13 May 2026 23:30:17 +0800
Subject: [PATCH 2/8] fix(spring-boot): Add Boot 4 profiles and smoke paths
---
dubbo-demo/dubbo-demo-mcp-server/pom.xml | 21 +-
.../dubbo-demo-spring-boot-servlet/pom.xml | 15 +-
dubbo-demo/dubbo-demo-spring-boot/pom.xml | 7 +
.../dubbo-spring-boot-3-autoconfigure/pom.xml | 104 +++++
.../dubbo-spring-boot-4-autoconfigure/pom.xml | 7 +
.../main/resources/META-INF/spring.factories | 2 +-
...DependencyCheckAutoConfigurationTests.java | 129 ++++++
.../DubboTriple4AutoConfigurationTests.java | 91 +++++
.../DubboZipkin4AutoConfigurationTests.java | 108 +++++
.../dubbo-spring-boot-autoconfigure/pom.xml | 114 ++++++
...Boot4DependencyCheckAutoConfiguration.java | 24 ++
.../SpringBootBefore4Condition.java | 3 +-
.../main/resources/META-INF/spring.factories | 3 +-
.../SpringBootConditionTests.java | 51 +++
.../dubbo-spring-boot-compatible/pom.xml | 77 ++++
.../pom.xml | 13 +
.../pom.xml | 13 +
.../dubbo-spring-boot/pom.xml | 5 +
.../org/apache/dubbo/dependency/FileTest.java | 378 ++++++++++++++++++
pom.xml | 112 +++++-
20 files changed, 1265 insertions(+), 12 deletions(-)
create mode 100644 dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/DubboSpringBoot4DependencyCheckAutoConfigurationTests.java
create mode 100644 dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTriple4AutoConfigurationTests.java
create mode 100644 dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/DubboZipkin4AutoConfigurationTests.java
create mode 100644 dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBootConditionTests.java
diff --git a/dubbo-demo/dubbo-demo-mcp-server/pom.xml b/dubbo-demo/dubbo-demo-mcp-server/pom.xml
index 23dcc288fb87..d0729f0a8868 100644
--- a/dubbo-demo/dubbo-demo-mcp-server/pom.xml
+++ b/dubbo-demo/dubbo-demo-mcp-server/pom.xml
@@ -137,10 +137,10 @@
- jdk-version-ge-17
-
- [17,)
-
+ spring-boot-3
+
+ ${spring-boot-3.version}
+
org.apache.dubbo
@@ -149,5 +149,18 @@
+
+ spring-boot-4
+
+ ${spring-boot-4.version}
+
+
+
+ org.apache.dubbo
+ dubbo-spring-boot-4-autoconfigure
+ ${project.version}
+
+
+
diff --git a/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/pom.xml b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/pom.xml
index 9996b76e7c6e..631e938bb930 100644
--- a/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/pom.xml
+++ b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/pom.xml
@@ -122,10 +122,7 @@
- jdk-version-ge-17
-
- [17,)
-
+ spring-boot-3
org.apache.dubbo
@@ -134,5 +131,15 @@
+
+ spring-boot-4
+
+
+ org.apache.dubbo
+ dubbo-spring-boot-4-autoconfigure
+ ${project.version}
+
+
+
diff --git a/dubbo-demo/dubbo-demo-spring-boot/pom.xml b/dubbo-demo/dubbo-demo-spring-boot/pom.xml
index 3fdf07d23a0a..46837f538f89 100644
--- a/dubbo-demo/dubbo-demo-spring-boot/pom.xml
+++ b/dubbo-demo/dubbo-demo-spring-boot/pom.xml
@@ -97,5 +97,12 @@
3.2.1
+
+ spring-boot-4
+
+ ${spring-boot-4.version}
+ ${spring-boot-4.version}
+
+
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-3-autoconfigure/pom.xml b/dubbo-spring-boot-project/dubbo-spring-boot-3-autoconfigure/pom.xml
index 99ea01813228..7ee3a91d2989 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-3-autoconfigure/pom.xml
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-3-autoconfigure/pom.xml
@@ -28,8 +28,112 @@
jar
Apache Dubbo Spring Boot 3 Auto-Configure
+
+ 10.1.52
+
+
+
+ org.springframework.boot
+ spring-boot
+ ${spring-boot-3.version}
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+ ${spring-boot-3.version}
+
+
+ org.springframework.boot
+ spring-boot-starter
+ ${spring-boot-3.version}
+
+
+ org.springframework.boot
+ spring-boot-test
+ ${spring-boot-3.version}
+
+
+ org.springframework.boot
+ spring-boot-test-autoconfigure
+ ${spring-boot-3.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ ${spring-boot-3.version}
+
+
+ org.springframework.boot
+ spring-boot-actuator
+ ${spring-boot-3.version}
+
+
+ org.springframework.boot
+ spring-boot-actuator-autoconfigure
+ ${spring-boot-3.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+ ${spring-boot-3.version}
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ ${spring-boot-3.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
+ ${spring-boot-3.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-json
+ ${spring-boot-3.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-log4j2
+ ${spring-boot-3.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+ ${spring-boot-3.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-tomcat
+ ${spring-boot-3.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+ ${spring-boot-3.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ ${spring-boot-3.version}
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-core
+ ${spring-boot-3.tomcat.version}
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-el
+ ${spring-boot-3.tomcat.version}
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-websocket
+ ${spring-boot-3.tomcat.version}
+
org.springframework.boot
spring-boot-dependencies
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/pom.xml b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/pom.xml
index 7c95584e7f62..f3a5f3ec699e 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/pom.xml
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/pom.xml
@@ -137,5 +137,12 @@
spring-webflux
true
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/resources/META-INF/spring.factories b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/resources/META-INF/spring.factories
index 9befabcd3f35..b1be32a5d2a3 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/resources/META-INF/spring.factories
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/resources/META-INF/spring.factories
@@ -1,3 +1,3 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.apache.dubbo.spring.boot.autoconfigure.DubboTriple4AutoConfiguration,\
- org.apache.dubbo.spring.boot.autoconfigure.observability.zipkin.DubboZipkin4AutoConfiguration
+org.apache.dubbo.spring.boot.autoconfigure.observability.zipkin.DubboZipkin4AutoConfiguration
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/DubboSpringBoot4DependencyCheckAutoConfigurationTests.java b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/DubboSpringBoot4DependencyCheckAutoConfigurationTests.java
new file mode 100644
index 000000000000..52935c7f4be4
--- /dev/null
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/DubboSpringBoot4DependencyCheckAutoConfigurationTests.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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.apache.dubbo.spring.boot.autoconfigure;
+
+import org.apache.dubbo.spring.boot.autoconfigure.observability.zipkin.DubboZipkin4AutoConfiguration;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.test.context.FilteredClassLoader;
+import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class DubboSpringBoot4DependencyCheckAutoConfigurationTests {
+
+ private static final String MISSING_BOOT4_AUTOCONFIGURE =
+ "Couldn't enable servlet support for triple at SpringBoot4: Missing dubbo-spring-boot-4-autoconfigure";
+
+ private static final String MISSING_BOOT4_ZIPKIN_AUTOCONFIGURE =
+ "Couldn't enable Zipkin tracing at SpringBoot4: Missing dubbo-spring-boot-4-autoconfigure";
+
+ private final boolean springBoot4 = SpringBoot4Condition.IS_SPRING_BOOT_4;
+
+ private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
+ .withConfiguration(AutoConfigurations.of(DubboSpringBoot4DependencyCheckAutoConfiguration.class))
+ .withClassLoader(
+ new FilteredClassLoader(DubboTriple4AutoConfiguration.class, DubboZipkin4AutoConfiguration.class));
+
+ private final WebApplicationContextRunner contextRunnerWithSpringBoot4Autoconfigure =
+ new WebApplicationContextRunner()
+ .withConfiguration(AutoConfigurations.of(DubboSpringBoot4DependencyCheckAutoConfiguration.class));
+
+ @BeforeEach
+ void enableSpringBoot4Condition() {
+ SpringBoot4Condition.IS_SPRING_BOOT_4 = true;
+ }
+
+ @AfterEach
+ void resetSpringBoot4Condition() {
+ SpringBoot4Condition.IS_SPRING_BOOT_4 = springBoot4;
+ }
+
+ @Test
+ void shouldFailWhenTripleServletEnabledWithoutSpringBoot4Autoconfigure() {
+ this.contextRunner
+ .withPropertyValues("dubbo.protocol.triple.servlet.enabled=true")
+ .run(context -> {
+ assertThat(context).hasFailed();
+ assertThat(context.getStartupFailure()).hasRootCauseMessage(MISSING_BOOT4_AUTOCONFIGURE);
+ });
+ }
+
+ @Test
+ void shouldFailWhenTripleWebSocketEnabledWithoutSpringBoot4Autoconfigure() {
+ this.contextRunner
+ .withPropertyValues("dubbo.protocol.triple.websocket.enabled=true")
+ .run(context -> {
+ assertThat(context).hasFailed();
+ assertThat(context.getStartupFailure()).hasRootCauseMessage(MISSING_BOOT4_AUTOCONFIGURE);
+ });
+ }
+
+ @Test
+ void shouldFailWhenZipkinEnabledWithoutSpringBoot4Autoconfigure() {
+ this.contextRunner
+ .withPropertyValues(
+ "dubbo.tracing.enabled=true",
+ "dubbo.tracing.tracing-exporter.zipkin-config.endpoint=http://localhost:9411/api/v2/spans")
+ .run(context -> {
+ assertThat(context).hasFailed();
+ assertThat(context.getStartupFailure()).hasRootCauseMessage(MISSING_BOOT4_ZIPKIN_AUTOCONFIGURE);
+ });
+ }
+
+ @Test
+ void shouldNotFailWhenSpringBoot4AutoconfigureIsPresent() {
+ this.contextRunnerWithSpringBoot4Autoconfigure
+ .withPropertyValues(
+ "dubbo.protocol.triple.servlet.enabled=true",
+ "dubbo.protocol.triple.websocket.enabled=true",
+ "dubbo.tracing.enabled=true",
+ "dubbo.tracing.tracing-exporter.zipkin-config.endpoint=http://localhost:9411/api/v2/spans")
+ .run(context -> assertThat(context).hasNotFailed());
+ }
+
+ @Test
+ void shouldNotFailWhenSpringBoot4ConditionIsDisabled() {
+ SpringBoot4Condition.IS_SPRING_BOOT_4 = false;
+
+ this.contextRunner
+ .withPropertyValues(
+ "dubbo.protocol.triple.servlet.enabled=true",
+ "dubbo.protocol.triple.websocket.enabled=true",
+ "dubbo.tracing.enabled=true",
+ "dubbo.tracing.tracing-exporter.zipkin-config.endpoint=http://localhost:9411/api/v2/spans")
+ .run(context -> assertThat(context).hasNotFailed());
+ }
+
+ @Test
+ void shouldNotFailWhenZipkinEndpointIsMissing() {
+ this.contextRunner.withPropertyValues("dubbo.tracing.enabled=true").run(context -> assertThat(context)
+ .hasNotFailed());
+ }
+
+ @Test
+ void shouldNotFailWhenTracingIsDisabled() {
+ this.contextRunner
+ .withPropertyValues(
+ "dubbo.tracing.enabled=false",
+ "dubbo.tracing.tracing-exporter.zipkin-config.endpoint=http://localhost:9411/api/v2/spans")
+ .run(context -> assertThat(context).hasNotFailed());
+ }
+}
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTriple4AutoConfigurationTests.java b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTriple4AutoConfigurationTests.java
new file mode 100644
index 000000000000..bc1b385d1795
--- /dev/null
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTriple4AutoConfigurationTests.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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.apache.dubbo.spring.boot.autoconfigure;
+
+import org.apache.dubbo.rpc.protocol.tri.servlet.jakarta.TripleFilter;
+import org.apache.dubbo.rpc.protocol.tri.websocket.jakarta.TripleWebSocketFilter;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
+import org.springframework.boot.web.server.WebServerFactoryCustomizer;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class DubboTriple4AutoConfigurationTests {
+
+ private final boolean springBoot4 = SpringBoot4Condition.IS_SPRING_BOOT_4;
+
+ private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
+ .withConfiguration(AutoConfigurations.of(DubboTriple4AutoConfiguration.class));
+
+ @BeforeEach
+ void enableSpringBoot4Condition() {
+ SpringBoot4Condition.IS_SPRING_BOOT_4 = true;
+ }
+
+ @AfterEach
+ void resetSpringBoot4Condition() {
+ SpringBoot4Condition.IS_SPRING_BOOT_4 = springBoot4;
+ }
+
+ @Test
+ void shouldRegisterTripleServletFilter() {
+ this.contextRunner
+ .withPropertyValues(
+ "dubbo.protocol.triple.servlet.enabled=true",
+ "dubbo.protocol.triple.servlet.filter-url-patterns=/triple/*",
+ "dubbo.protocol.triple.servlet.filter-order=-100",
+ "server.port=20880")
+ .run(context -> {
+ assertThat(context).hasSingleBean(FilterRegistrationBean.class);
+ FilterRegistrationBean> registrationBean = context.getBean(FilterRegistrationBean.class);
+ assertThat(registrationBean.getFilter()).isInstanceOf(TripleFilter.class);
+ assertThat(registrationBean.getUrlPatterns()).containsExactly("/triple/*");
+ assertThat(registrationBean.getOrder()).isEqualTo(-100);
+ });
+ }
+
+ @Test
+ void shouldRegisterTripleWebSocketFilter() {
+ this.contextRunner
+ .withPropertyValues(
+ "dubbo.protocol.triple.websocket.enabled=true",
+ "dubbo.protocol.triple.websocket.filter-url-patterns=/websocket/*",
+ "dubbo.protocol.triple.websocket.filter-order=-200",
+ "server.port=20881")
+ .run(context -> {
+ assertThat(context).hasSingleBean(FilterRegistrationBean.class);
+ FilterRegistrationBean> registrationBean = context.getBean(FilterRegistrationBean.class);
+ assertThat(registrationBean.getFilter()).isInstanceOf(TripleWebSocketFilter.class);
+ assertThat(registrationBean.getUrlPatterns()).containsExactly("/websocket/*");
+ assertThat(registrationBean.getOrder()).isEqualTo(-200);
+ });
+ }
+
+ @Test
+ void shouldRegisterTomcatHttp2CustomizerWhenMaxConcurrentStreamsConfigured() {
+ this.contextRunner
+ .withPropertyValues(
+ "dubbo.protocol.triple.servlet.enabled=true",
+ "dubbo.protocol.triple.servlet.max-concurrent-streams=10")
+ .run(context -> assertThat(context).hasSingleBean(WebServerFactoryCustomizer.class));
+ }
+}
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/DubboZipkin4AutoConfigurationTests.java b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/DubboZipkin4AutoConfigurationTests.java
new file mode 100644
index 000000000000..f58894a6a04e
--- /dev/null
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/DubboZipkin4AutoConfigurationTests.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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.apache.dubbo.spring.boot.autoconfigure.observability.zipkin;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import io.opentelemetry.exporter.zipkin.ZipkinSpanExporter;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.test.context.FilteredClassLoader;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.reactive.function.client.WebClient;
+import zipkin2.Span;
+import zipkin2.reporter.AsyncReporter;
+import zipkin2.reporter.Call;
+import zipkin2.reporter.Encoding;
+import zipkin2.reporter.Sender;
+import zipkin2.reporter.urlconnection.URLConnectionSender;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class DubboZipkin4AutoConfigurationTests {
+
+ private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
+ .withConfiguration(AutoConfigurations.of(DubboZipkin4AutoConfiguration.class))
+ .withPropertyValues(
+ "dubbo.tracing.enabled=true",
+ "dubbo.tracing.tracing-exporter.zipkin-config.endpoint=http://localhost:9411/api/v2/spans");
+
+ @Test
+ void shouldSupplyZipkinBeansOnSpringBoot4() {
+ this.contextRunner
+ .withUserConfiguration(NoOpReporterConfiguration.class, NoOpSenderConfiguration.class)
+ .run((context) -> {
+ assertThat(context).hasSingleBean(Sender.class);
+ assertThat(context).hasSingleBean(ZipkinSpanExporter.class);
+ });
+ }
+
+ @Test
+ void shouldSupplyRestTemplateSenderWhenUrlConnectionAndWebClientAreUnavailable() {
+ this.contextRunner
+ .withClassLoader(new FilteredClassLoader(URLConnectionSender.class, WebClient.class))
+ .withUserConfiguration(NoOpReporterConfiguration.class)
+ .run((context) -> {
+ assertThat(context).hasSingleBean(Sender.class);
+ assertThat(context.getBean(Sender.class)).isInstanceOf(Zipkin4RestTemplateSender.class);
+ assertThat(context).hasSingleBean(ZipkinSpanExporter.class);
+ });
+ }
+
+ @Configuration(proxyBeanMethods = false)
+ private static class NoOpReporterConfiguration {
+
+ @Bean
+ AsyncReporter spanReporter(Sender sender, zipkin2.reporter.BytesEncoder encoder) {
+ return AsyncReporter.builder(sender)
+ .messageTimeout(0, TimeUnit.NANOSECONDS)
+ .build(encoder);
+ }
+ }
+
+ @Configuration(proxyBeanMethods = false)
+ private static class NoOpSenderConfiguration {
+
+ @Bean
+ Sender sender() {
+ return new Sender() {
+ @Override
+ public Encoding encoding() {
+ return Encoding.JSON;
+ }
+
+ @Override
+ public int messageMaxBytes() {
+ return Integer.MAX_VALUE;
+ }
+
+ @Override
+ public int messageSizeInBytes(List encodedSpans) {
+ return encodedSpans.stream().mapToInt(bytes -> bytes.length).sum();
+ }
+
+ @Override
+ public Call sendSpans(List encodedSpans) {
+ return Call.create(null);
+ }
+ };
+ }
+ }
+}
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/pom.xml b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/pom.xml
index 6b98ad05010f..fbb2ad9b0d08 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/pom.xml
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/pom.xml
@@ -347,4 +347,118 @@
+
+
+
+ spring-boot-4
+
+ 2.7.18
+ 9.0.117
+
+
+
+
+ org.springframework.boot
+ spring-boot
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-starter
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-test
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-test-autoconfigure
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-actuator
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-actuator-autoconfigure
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-json
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-log4j2
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-tomcat
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ ${spring-boot-2.version}
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-core
+ ${spring-boot-2.tomcat.version}
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-el
+ ${spring-boot-2.tomcat.version}
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-websocket
+ ${spring-boot-2.tomcat.version}
+
+
+
+
+
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboSpringBoot4DependencyCheckAutoConfiguration.java b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboSpringBoot4DependencyCheckAutoConfiguration.java
index cbc6b00886b0..541e93f08fee 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboSpringBoot4DependencyCheckAutoConfiguration.java
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboSpringBoot4DependencyCheckAutoConfiguration.java
@@ -16,6 +16,8 @@
*/
package org.apache.dubbo.spring.boot.autoconfigure;
+import org.apache.dubbo.spring.boot.autoconfigure.observability.annotation.ConditionalOnDubboTracingEnable;
+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -35,12 +37,22 @@ public class DubboSpringBoot4DependencyCheckAutoConfiguration {
public static final String JAKARATA_SERVLET_FILTER = "jakarta.servlet.Filter";
+ public static final String ZIPKIN_SENDER = "zipkin2.reporter.Sender";
+
+ public static final String ZIPKIN_CONFIG_PREFIX = "dubbo.tracing.tracing-exporter.zipkin-config";
+
public static final String DUBBO_TRIPLE_4_AUTOCONFIGURATION =
"org.apache.dubbo.spring.boot.autoconfigure.DubboTriple4AutoConfiguration";
+ public static final String DUBBO_ZIPKIN_4_AUTOCONFIGURATION =
+ "org.apache.dubbo.spring.boot.autoconfigure.observability.zipkin.DubboZipkin4AutoConfiguration";
+
private static final String SPRING_BOOT_4_DEPENDENCY_CHECK_WARNING =
"Couldn't enable servlet support for triple at SpringBoot4: Missing dubbo-spring-boot-4-autoconfigure";
+ private static final String SPRING_BOOT_4_ZIPKIN_DEPENDENCY_CHECK_WARNING =
+ "Couldn't enable Zipkin tracing at SpringBoot4: Missing dubbo-spring-boot-4-autoconfigure";
+
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = JAKARATA_SERVLET_FILTER)
@ConditionalOnWebApplication(type = Type.SERVLET)
@@ -64,4 +76,16 @@ public Object tripleWebSocketFilterDependencyCheck() {
throw new IllegalStateException(SPRING_BOOT_4_DEPENDENCY_CHECK_WARNING);
}
}
+
+ @Configuration(proxyBeanMethods = false)
+ @ConditionalOnClass(name = ZIPKIN_SENDER)
+ @ConditionalOnProperty(prefix = ZIPKIN_CONFIG_PREFIX, name = "endpoint")
+ @ConditionalOnDubboTracingEnable
+ @ConditionalOnMissingClass(DUBBO_ZIPKIN_4_AUTOCONFIGURATION)
+ public static class zipkinDependencyCheckConfiguration {
+ @Bean
+ public Object zipkinDependencyCheck() {
+ throw new IllegalStateException(SPRING_BOOT_4_ZIPKIN_DEPENDENCY_CHECK_WARNING);
+ }
+ }
}
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBootBefore4Condition.java b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBootBefore4Condition.java
index 99505cc750a8..144363813275 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBootBefore4Condition.java
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBootBefore4Condition.java
@@ -23,7 +23,8 @@
public class SpringBootBefore4Condition implements Condition {
- public static boolean IS_SPRING_BOOT_BEFORE_4 = SpringBootVersion.getVersion().charAt(0) < '4';
+ public static boolean IS_SPRING_BOOT_BEFORE_4 =
+ SpringBootVersion.getVersion().charAt(0) < '4';
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories
index 5256a723cd8e..a72642e67b01 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories
@@ -9,4 +9,5 @@ org.apache.dubbo.spring.boot.autoconfigure.observability.DubboObservationAutoCon
org.apache.dubbo.spring.boot.autoconfigure.observability.brave.BraveAutoConfiguration,\
org.apache.dubbo.spring.boot.autoconfigure.observability.zipkin.ZipkinAutoConfiguration,\
org.apache.dubbo.spring.boot.autoconfigure.observability.otlp.OtlpAutoConfiguration,\
-org.apache.dubbo.spring.boot.autoconfigure.DubboSpringBoot3DependencyCheckAutoConfiguration
+org.apache.dubbo.spring.boot.autoconfigure.DubboSpringBoot3DependencyCheckAutoConfiguration,\
+org.apache.dubbo.spring.boot.autoconfigure.DubboSpringBoot4DependencyCheckAutoConfiguration
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBootConditionTests.java b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBootConditionTests.java
new file mode 100644
index 000000000000..d51bd5cca6ff
--- /dev/null
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBootConditionTests.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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.apache.dubbo.spring.boot.autoconfigure;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+class SpringBootConditionTests {
+
+ private final boolean springBootBefore4 = SpringBootBefore4Condition.IS_SPRING_BOOT_BEFORE_4;
+ private final boolean springBoot4 = SpringBoot4Condition.IS_SPRING_BOOT_4;
+
+ @AfterEach
+ void resetConditions() {
+ SpringBootBefore4Condition.IS_SPRING_BOOT_BEFORE_4 = springBootBefore4;
+ SpringBoot4Condition.IS_SPRING_BOOT_4 = springBoot4;
+ }
+
+ @Test
+ void shouldMatchOnlyBefore4ConditionBeforeSpringBoot4() {
+ SpringBootBefore4Condition.IS_SPRING_BOOT_BEFORE_4 = true;
+ SpringBoot4Condition.IS_SPRING_BOOT_4 = false;
+
+ Assertions.assertTrue(new SpringBootBefore4Condition().matches(null, null));
+ Assertions.assertFalse(new SpringBoot4Condition().matches(null, null));
+ }
+
+ @Test
+ void shouldMatchOnlySpringBoot4ConditionOnSpringBoot4() {
+ SpringBootBefore4Condition.IS_SPRING_BOOT_BEFORE_4 = false;
+ SpringBoot4Condition.IS_SPRING_BOOT_4 = true;
+
+ Assertions.assertFalse(new SpringBootBefore4Condition().matches(null, null));
+ Assertions.assertTrue(new SpringBoot4Condition().matches(null, null));
+ }
+}
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-compatible/pom.xml b/dubbo-spring-boot-project/dubbo-spring-boot-compatible/pom.xml
index 0b9bf7cbda23..bb332ea98de2 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-compatible/pom.xml
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-compatible/pom.xml
@@ -35,6 +35,7 @@
1.5.22.RELEASE
+ 8.5.43
4.11.0
@@ -46,6 +47,81 @@
${mockito.version}
test
+
+ org.springframework.boot
+ spring-boot
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-starter
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-test
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-test-autoconfigure
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-actuator
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-log4j2
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-tomcat
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ ${spring-boot.version}
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-core
+ ${spring-boot.tomcat.version}
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-el
+ ${spring-boot.tomcat.version}
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-websocket
+ ${spring-boot.tomcat.version}
+
org.springframework.boot
spring-boot-dependencies
@@ -92,6 +168,7 @@
spring-boot-1.4
1.4.7.RELEASE
+ 8.5.15
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-starters/dubbo-tracing-brave-zipkin-spring-boot-starter/pom.xml b/dubbo-spring-boot-project/dubbo-spring-boot-starters/dubbo-tracing-brave-zipkin-spring-boot-starter/pom.xml
index 7de16bdbde59..7d4619e330e0 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-starters/dubbo-tracing-brave-zipkin-spring-boot-starter/pom.xml
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-starters/dubbo-tracing-brave-zipkin-spring-boot-starter/pom.xml
@@ -52,4 +52,17 @@
+
+
+ spring-boot-4
+
+
+ org.apache.dubbo
+ dubbo-spring-boot-4-autoconfigure
+ ${project.version}
+
+
+
+
+
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-starters/dubbo-tracing-otel-zipkin-spring-boot-starter/pom.xml b/dubbo-spring-boot-project/dubbo-spring-boot-starters/dubbo-tracing-otel-zipkin-spring-boot-starter/pom.xml
index b999ce847b51..222ad71db8f7 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-starters/dubbo-tracing-otel-zipkin-spring-boot-starter/pom.xml
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-starters/dubbo-tracing-otel-zipkin-spring-boot-starter/pom.xml
@@ -44,4 +44,17 @@
+
+
+ spring-boot-4
+
+
+ org.apache.dubbo
+ dubbo-spring-boot-4-autoconfigure
+ ${project.version}
+
+
+
+
+
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot/pom.xml b/dubbo-spring-boot-project/dubbo-spring-boot/pom.xml
index a43652782d67..addc5752c157 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot/pom.xml
+++ b/dubbo-spring-boot-project/dubbo-spring-boot/pom.xml
@@ -52,6 +52,11 @@
spring-boot-starter-web
true
+
+ javax.servlet
+ javax.servlet-api
+ provided
+
org.apache.dubbo
dubbo-rpc-api
diff --git a/dubbo-test/dubbo-test-modules/src/test/java/org/apache/dubbo/dependency/FileTest.java b/dubbo-test/dubbo-test-modules/src/test/java/org/apache/dubbo/dependency/FileTest.java
index 44c9b52f4fdf..8e2a2dfa4f95 100644
--- a/dubbo-test/dubbo-test-modules/src/test/java/org/apache/dubbo/dependency/FileTest.java
+++ b/dubbo-test/dubbo-test-modules/src/test/java/org/apache/dubbo/dependency/FileTest.java
@@ -210,6 +210,322 @@ void checkDubboDependenciesAll() throws DocumentException {
"Newly created modules must be added to dubbo-dependencies-all. Found modules: " + expectedArtifactIds);
}
+ @Test
+ void checkZipkinSpringBootStartersSupportSpringBoot4() throws DocumentException {
+ File baseFile = getBaseFile();
+ SAXReader reader = new SAXReader();
+ List zipkinStarters = new LinkedList<>();
+ zipkinStarters.add("dubbo-tracing-brave-zipkin-spring-boot-starter");
+ zipkinStarters.add("dubbo-tracing-otel-zipkin-spring-boot-starter");
+
+ for (String zipkinStarter : zipkinStarters) {
+ String pomPath = "dubbo-spring-boot-project" + File.separator + "dubbo-spring-boot-starters"
+ + File.separator + zipkinStarter + File.separator + "pom.xml";
+ Document document = reader.read(new File(baseFile, pomPath));
+
+ Assertions.assertTrue(
+ hasProfileDependency(
+ document.getRootElement(), "spring-boot-4", "dubbo-spring-boot-4-autoconfigure"),
+ zipkinStarter + " must depend on dubbo-spring-boot-4-autoconfigure for Spring Boot 4");
+ Assertions.assertFalse(
+ hasProfileDependency(
+ document.getRootElement(), "jdk-version-ge-17", "dubbo-spring-boot-4-autoconfigure"),
+ zipkinStarter + " must not enable Spring Boot 4 autoconfigure only because the JDK is 17+");
+ }
+ }
+
+ @Test
+ void checkSpringBootDemosSupportSpringBoot4SmokeCompile() throws DocumentException {
+ File baseFile = getBaseFile();
+ SAXReader reader = new SAXReader();
+ List demoPomPaths = new LinkedList<>();
+ demoPomPaths.add("dubbo-demo" + File.separator + "dubbo-demo-spring-boot" + File.separator
+ + "dubbo-demo-spring-boot-servlet" + File.separator + "pom.xml");
+ demoPomPaths.add("dubbo-demo" + File.separator + "dubbo-demo-mcp-server" + File.separator + "pom.xml");
+
+ for (String demoPomPath : demoPomPaths) {
+ Document document = reader.read(new File(baseFile, demoPomPath));
+
+ Assertions.assertTrue(
+ hasProfileDependency(
+ document.getRootElement(), "spring-boot-4", "dubbo-spring-boot-4-autoconfigure"),
+ demoPomPath + " must depend on dubbo-spring-boot-4-autoconfigure for Spring Boot 4 smoke compile");
+ Assertions.assertTrue(
+ hasProfileDependency(
+ document.getRootElement(), "spring-boot-3", "dubbo-spring-boot-3-autoconfigure"),
+ demoPomPath + " must depend on dubbo-spring-boot-3-autoconfigure only for Spring Boot 3");
+ Assertions.assertFalse(
+ hasProfileDependency(
+ document.getRootElement(), "jdk-version-ge-17", "dubbo-spring-boot-3-autoconfigure"),
+ demoPomPath + " must not enable Spring Boot 3 autoconfigure only because the JDK is 17+");
+ }
+ }
+
+ @Test
+ void checkSpringBootDependencyCheckAutoConfigurationsRegistered() throws IOException {
+ File baseFile = getBaseFile();
+ String autoConfigurationImportsPath = "dubbo-spring-boot-project" + File.separator
+ + "dubbo-spring-boot-autoconfigure" + File.separator + "src" + File.separator + "main"
+ + File.separator + "resources" + File.separator + "META-INF" + File.separator + "spring"
+ + File.separator + "org.springframework.boot.autoconfigure.AutoConfiguration.imports";
+ String springFactoriesPath = "dubbo-spring-boot-project" + File.separator + "dubbo-spring-boot-autoconfigure"
+ + File.separator + "src" + File.separator + "main" + File.separator + "resources" + File.separator
+ + "META-INF" + File.separator + "spring.factories";
+
+ String[] dependencyCheckAutoConfigurations = {
+ "org.apache.dubbo.spring.boot.autoconfigure.DubboSpringBoot3DependencyCheckAutoConfiguration",
+ "org.apache.dubbo.spring.boot.autoconfigure.DubboSpringBoot4DependencyCheckAutoConfiguration"
+ };
+ for (String autoConfiguration : dependencyCheckAutoConfigurations) {
+ Assertions.assertTrue(
+ hasResourceEntry(baseFile, autoConfigurationImportsPath, autoConfiguration),
+ autoConfiguration + " must be registered in AutoConfiguration.imports");
+ Assertions.assertTrue(
+ hasResourceEntry(baseFile, springFactoriesPath, autoConfiguration),
+ autoConfiguration + " must be registered in spring.factories");
+ }
+ }
+
+ @Test
+ void checkSpringBoot4AutoConfigurationResources() throws IOException {
+ File baseFile = getBaseFile();
+ String autoConfigurationImportsPath = "dubbo-spring-boot-project" + File.separator
+ + "dubbo-spring-boot-4-autoconfigure" + File.separator + "src" + File.separator + "main"
+ + File.separator + "resources" + File.separator + "META-INF" + File.separator + "spring"
+ + File.separator + "org.springframework.boot.autoconfigure.AutoConfiguration.imports";
+ String springFactoriesPath = "dubbo-spring-boot-project" + File.separator + "dubbo-spring-boot-4-autoconfigure"
+ + File.separator + "src" + File.separator + "main" + File.separator + "resources" + File.separator
+ + "META-INF" + File.separator + "spring.factories";
+
+ String[] springBoot4AutoConfigurations = {
+ "org.apache.dubbo.spring.boot.autoconfigure.DubboTriple4AutoConfiguration",
+ "org.apache.dubbo.spring.boot.autoconfigure.observability.zipkin.DubboZipkin4AutoConfiguration"
+ };
+ for (String autoConfiguration : springBoot4AutoConfigurations) {
+ Assertions.assertTrue(
+ hasResourceEntry(baseFile, autoConfigurationImportsPath, autoConfiguration),
+ autoConfiguration + " must be registered in AutoConfiguration.imports");
+ Assertions.assertTrue(
+ hasResourceEntry(baseFile, springFactoriesPath, autoConfiguration),
+ autoConfiguration + " must be registered in spring.factories");
+ }
+ }
+
+ @Test
+ void checkSpringBoot4StarterDependsOnAutoConfigurations() throws DocumentException {
+ File baseFile = getBaseFile();
+ SAXReader reader = new SAXReader();
+ String springBoot4StarterPath = "dubbo-spring-boot-project" + File.separator + "dubbo-spring-boot-starters"
+ + File.separator + "dubbo-spring-boot-4-starter" + File.separator + "pom.xml";
+ Element rootElement =
+ reader.read(new File(baseFile, springBoot4StarterPath)).getRootElement();
+
+ Assertions.assertTrue(
+ hasDependency(rootElement, "dubbo-spring-boot-autoconfigure"),
+ "dubbo-spring-boot-4-starter must depend on dubbo-spring-boot-autoconfigure");
+ Assertions.assertTrue(
+ hasDependency(rootElement, "dubbo-spring-boot-4-autoconfigure"),
+ "dubbo-spring-boot-4-starter must depend on dubbo-spring-boot-4-autoconfigure");
+ }
+
+ @Test
+ void checkDubboDependenciesAllSupportSpringBoot4() throws DocumentException {
+ File baseFile = getBaseFile();
+ SAXReader reader = new SAXReader();
+ String dubboDependenciesAllPath =
+ "dubbo-test" + File.separator + "dubbo-dependencies-all" + File.separator + "pom.xml";
+ Element rootElement =
+ reader.read(new File(baseFile, dubboDependenciesAllPath)).getRootElement();
+
+ Assertions.assertTrue(
+ hasProfileDependency(rootElement, "jdk-version-ge-17", "dubbo-spring-boot-4-autoconfigure"),
+ "dubbo-dependencies-all must include dubbo-spring-boot-4-autoconfigure on JDK 17+");
+ Assertions.assertTrue(
+ hasProfileDependency(rootElement, "jdk-version-ge-17", "dubbo-spring-boot-4-starter"),
+ "dubbo-dependencies-all must include dubbo-spring-boot-4-starter on JDK 17+");
+ }
+
+ @Test
+ void checkRootSpringBoot4Profile() throws DocumentException {
+ File baseFile = getBaseFile();
+ SAXReader reader = new SAXReader();
+ Element rootElement = reader.read(new File(baseFile, "pom.xml")).getRootElement();
+
+ String[] springBootArtifacts = {
+ "spring-boot",
+ "spring-boot-autoconfigure",
+ "spring-boot-starter",
+ "spring-boot-test",
+ "spring-boot-test-autoconfigure",
+ "spring-boot-starter-test",
+ "spring-boot-actuator",
+ "spring-boot-actuator-autoconfigure",
+ "spring-boot-starter-actuator",
+ "spring-boot-configuration-processor",
+ "spring-boot-starter-aop",
+ "spring-boot-starter-json",
+ "spring-boot-starter-log4j2",
+ "spring-boot-starter-logging",
+ "spring-boot-starter-tomcat",
+ "spring-boot-starter-validation",
+ "spring-boot-starter-web"
+ };
+ for (String artifactId : springBootArtifacts) {
+ Assertions.assertTrue(
+ hasProfileManagedDependency(
+ rootElement,
+ "spring-boot-4",
+ "org.springframework.boot",
+ artifactId,
+ "${spring-boot-4.version}"),
+ "spring-boot-4 profile must manage " + artifactId + " with ${spring-boot-4.version}");
+ }
+
+ String[] tomcatArtifacts = {"tomcat-embed-core", "tomcat-embed-el", "tomcat-embed-websocket"};
+ for (String artifactId : tomcatArtifacts) {
+ Assertions.assertTrue(
+ hasProfileManagedDependency(
+ rootElement, "spring-boot-4", "org.apache.tomcat.embed", artifactId, "${tomcat.version}"),
+ "spring-boot-4 profile must manage " + artifactId + " with ${tomcat.version}");
+ }
+ }
+
+ @Test
+ void checkSpringBootCompatibleKeepsSpringBoot1DependencyManagement() throws DocumentException {
+ File baseFile = getBaseFile();
+ SAXReader reader = new SAXReader();
+ String springBootCompatiblePath = "dubbo-spring-boot-project" + File.separator + "dubbo-spring-boot-compatible"
+ + File.separator + "pom.xml";
+ Element rootElement =
+ reader.read(new File(baseFile, springBootCompatiblePath)).getRootElement();
+
+ String[] springBoot1Artifacts = {
+ "spring-boot",
+ "spring-boot-autoconfigure",
+ "spring-boot-configuration-processor",
+ "spring-boot-starter",
+ "spring-boot-test",
+ "spring-boot-test-autoconfigure",
+ "spring-boot-actuator",
+ "spring-boot-starter-actuator",
+ "spring-boot-starter-log4j2",
+ "spring-boot-starter-tomcat",
+ "spring-boot-starter-test",
+ "spring-boot-starter-web"
+ };
+ for (String artifactId : springBoot1Artifacts) {
+ Assertions.assertTrue(
+ hasManagedDependency(rootElement, "org.springframework.boot", artifactId, "${spring-boot.version}"),
+ "spring-boot-compatible must manage " + artifactId + " with ${spring-boot.version}");
+ }
+
+ String[] tomcat8Artifacts = {"tomcat-embed-core", "tomcat-embed-el", "tomcat-embed-websocket"};
+ for (String artifactId : tomcat8Artifacts) {
+ Assertions.assertTrue(
+ hasManagedDependency(
+ rootElement, "org.apache.tomcat.embed", artifactId, "${spring-boot.tomcat.version}"),
+ "spring-boot-compatible must manage " + artifactId + " with ${spring-boot.tomcat.version}");
+ }
+ }
+
+ @Test
+ void checkSpringBoot3AutoconfigureKeepsSpringBoot3DependencyManagement() throws DocumentException {
+ File baseFile = getBaseFile();
+ SAXReader reader = new SAXReader();
+ String springBoot3AutoconfigurePath = "dubbo-spring-boot-project" + File.separator
+ + "dubbo-spring-boot-3-autoconfigure" + File.separator + "pom.xml";
+ Element rootElement =
+ reader.read(new File(baseFile, springBoot3AutoconfigurePath)).getRootElement();
+
+ String[] springBoot3Artifacts = {
+ "spring-boot",
+ "spring-boot-autoconfigure",
+ "spring-boot-starter",
+ "spring-boot-test",
+ "spring-boot-test-autoconfigure",
+ "spring-boot-starter-test",
+ "spring-boot-actuator",
+ "spring-boot-actuator-autoconfigure",
+ "spring-boot-starter-actuator",
+ "spring-boot-configuration-processor",
+ "spring-boot-starter-aop",
+ "spring-boot-starter-json",
+ "spring-boot-starter-log4j2",
+ "spring-boot-starter-logging",
+ "spring-boot-starter-tomcat",
+ "spring-boot-starter-validation",
+ "spring-boot-starter-web"
+ };
+ for (String artifactId : springBoot3Artifacts) {
+ Assertions.assertTrue(
+ hasManagedDependency(
+ rootElement, "org.springframework.boot", artifactId, "${spring-boot-3.version}"),
+ "dubbo-spring-boot-3-autoconfigure must manage " + artifactId + " with ${spring-boot-3.version}");
+ }
+ String[] tomcat10Artifacts = {"tomcat-embed-core", "tomcat-embed-el", "tomcat-embed-websocket"};
+ for (String artifactId : tomcat10Artifacts) {
+ Assertions.assertTrue(
+ hasManagedDependency(
+ rootElement, "org.apache.tomcat.embed", artifactId, "${spring-boot-3.tomcat.version}"),
+ "dubbo-spring-boot-3-autoconfigure must manage " + artifactId
+ + " with ${spring-boot-3.tomcat.version}");
+ }
+ }
+
+ @Test
+ void checkSpringBootAutoconfigureKeepsBefore4DependencyManagement() throws DocumentException {
+ File baseFile = getBaseFile();
+ SAXReader reader = new SAXReader();
+ String springBootAutoconfigurePath = "dubbo-spring-boot-project" + File.separator
+ + "dubbo-spring-boot-autoconfigure" + File.separator + "pom.xml";
+ Element rootElement =
+ reader.read(new File(baseFile, springBootAutoconfigurePath)).getRootElement();
+
+ String[] springBootBefore4Artifacts = {
+ "spring-boot",
+ "spring-boot-autoconfigure",
+ "spring-boot-starter",
+ "spring-boot-test",
+ "spring-boot-test-autoconfigure",
+ "spring-boot-starter-test",
+ "spring-boot-actuator",
+ "spring-boot-actuator-autoconfigure",
+ "spring-boot-starter-actuator",
+ "spring-boot-configuration-processor",
+ "spring-boot-starter-aop",
+ "spring-boot-starter-json",
+ "spring-boot-starter-log4j2",
+ "spring-boot-starter-logging",
+ "spring-boot-starter-tomcat",
+ "spring-boot-starter-validation",
+ "spring-boot-starter-web"
+ };
+ for (String artifactId : springBootBefore4Artifacts) {
+ Assertions.assertTrue(
+ hasProfileManagedDependency(
+ rootElement,
+ "spring-boot-4",
+ "org.springframework.boot",
+ artifactId,
+ "${spring-boot-2.version}"),
+ "dubbo-spring-boot-autoconfigure must manage " + artifactId
+ + " with ${spring-boot-2.version} under spring-boot-4");
+ }
+
+ String[] tomcatBefore4Artifacts = {"tomcat-embed-core", "tomcat-embed-el", "tomcat-embed-websocket"};
+ for (String artifactId : tomcatBefore4Artifacts) {
+ Assertions.assertTrue(
+ hasProfileManagedDependency(
+ rootElement,
+ "spring-boot-4",
+ "org.apache.tomcat.embed",
+ artifactId,
+ "${spring-boot-2.tomcat.version}"),
+ "dubbo-spring-boot-autoconfigure must manage " + artifactId
+ + " with ${spring-boot-2.tomcat.version} under spring-boot-4");
+ }
+ }
+
@Test
void checkDubboAllDependencies() throws DocumentException {
File baseFile = getBaseFile();
@@ -819,4 +1135,66 @@ public void readSPIUnexpectedResource(File path, List spis) {
}
}
}
+
+ private boolean hasProfileDependency(Element rootElement, String profileId, String artifactId) {
+ Element profiles = rootElement.element("profiles");
+ if (profiles == null) {
+ return false;
+ }
+ return profiles.elements("profile").stream()
+ .filter(profile -> Objects.equals(profileId, profile.elementText("id")))
+ .map(profile -> profile.element("dependencies"))
+ .filter(Objects::nonNull)
+ .map(dependencies -> dependencies.elements("dependency"))
+ .flatMap(Collection::stream)
+ .anyMatch(dependency -> Objects.equals(artifactId, dependency.elementText("artifactId")));
+ }
+
+ private boolean hasDependency(Element rootElement, String artifactId) {
+ Element dependencies = rootElement.element("dependencies");
+ if (dependencies == null) {
+ return false;
+ }
+ return dependencies.elements("dependency").stream()
+ .anyMatch(dependency -> Objects.equals(artifactId, dependency.elementText("artifactId")));
+ }
+
+ private boolean hasManagedDependency(Element rootElement, String groupId, String artifactId, String version) {
+ Element dependencyManagement = rootElement.element("dependencyManagement");
+ if (dependencyManagement == null) {
+ return false;
+ }
+ Element dependencies = dependencyManagement.element("dependencies");
+ if (dependencies == null) {
+ return false;
+ }
+ return dependencies.elements("dependency").stream()
+ .anyMatch(dependency -> Objects.equals(groupId, dependency.elementText("groupId"))
+ && Objects.equals(artifactId, dependency.elementText("artifactId"))
+ && Objects.equals(version, dependency.elementText("version")));
+ }
+
+ private boolean hasProfileManagedDependency(
+ Element rootElement, String profileId, String groupId, String artifactId, String version) {
+ Element profiles = rootElement.element("profiles");
+ if (profiles == null) {
+ return false;
+ }
+ return profiles.elements("profile").stream()
+ .filter(profile -> Objects.equals(profileId, profile.elementText("id")))
+ .map(profile -> profile.element("dependencyManagement"))
+ .filter(Objects::nonNull)
+ .map(dependencyManagement -> dependencyManagement.element("dependencies"))
+ .filter(Objects::nonNull)
+ .map(dependencies -> dependencies.elements("dependency"))
+ .flatMap(Collection::stream)
+ .anyMatch(dependency -> Objects.equals(groupId, dependency.elementText("groupId"))
+ && Objects.equals(artifactId, dependency.elementText("artifactId"))
+ && Objects.equals(version, dependency.elementText("version")));
+ }
+
+ private boolean hasResourceEntry(File baseFile, String resourcePath, String resourceEntry) throws IOException {
+ return FileUtils.readFileToString(new File(baseFile, resourcePath), StandardCharsets.UTF_8)
+ .contains(resourceEntry);
+ }
}
diff --git a/pom.xml b/pom.xml
index 2363d9d67564..54e4eaf1e872 100644
--- a/pom.xml
+++ b/pom.xml
@@ -163,7 +163,7 @@
0.6.1
3.0.2
-
+
6.2.18
6.5.10
3.5.14
@@ -1214,5 +1214,115 @@
+
+ spring-boot-4
+
+ 11.0.21
+
+
+
+
+ org.springframework.boot
+ spring-boot
+ ${spring-boot-4.version}
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+ ${spring-boot-4.version}
+
+
+ org.springframework.boot
+ spring-boot-starter
+ ${spring-boot-4.version}
+
+
+ org.springframework.boot
+ spring-boot-test
+ ${spring-boot-4.version}
+
+
+ org.springframework.boot
+ spring-boot-test-autoconfigure
+ ${spring-boot-4.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ ${spring-boot-4.version}
+
+
+ org.springframework.boot
+ spring-boot-actuator
+ ${spring-boot-4.version}
+
+
+ org.springframework.boot
+ spring-boot-actuator-autoconfigure
+ ${spring-boot-4.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+ ${spring-boot-4.version}
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ ${spring-boot-4.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
+ ${spring-boot-4.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-json
+ ${spring-boot-4.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-log4j2
+ ${spring-boot-4.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+ ${spring-boot-4.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-tomcat
+ ${spring-boot-4.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+ ${spring-boot-4.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ ${spring-boot-4.version}
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-core
+ ${tomcat.version}
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-el
+ ${tomcat.version}
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-websocket
+ ${tomcat.version}
+
+
+
+
From 8e171e6d97c2ed65cd6d0b951136e072f421af05 Mon Sep 17 00:00:00 2001
From: SavitarC <71271200+savitarc@users.noreply.github.com>
Date: Sat, 16 May 2026 18:11:07 +0800
Subject: [PATCH 3/8] fix(spring-boot): Guard Zipkin RestTemplateBuilder on
Boot 4
---
.../observability/zipkin/Zipkin4Configurations.java | 2 +-
.../zipkin/DubboZipkin4AutoConfigurationTests.java | 13 +++++++++++++
2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/Zipkin4Configurations.java b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/Zipkin4Configurations.java
index f48d84618df0..50c1e788f6ff 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/Zipkin4Configurations.java
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/Zipkin4Configurations.java
@@ -79,7 +79,7 @@ URLConnectionSender urlConnectionSender(DubboConfigurationProperties properties)
}
@Configuration(proxyBeanMethods = false)
- @ConditionalOnClass(RestTemplate.class)
+ @ConditionalOnClass({RestTemplate.class, RestTemplateBuilder.class})
@EnableConfigurationProperties(DubboConfigurationProperties.class)
static class RestTemplateSenderConfiguration {
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/DubboZipkin4AutoConfigurationTests.java b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/DubboZipkin4AutoConfigurationTests.java
index f58894a6a04e..0990559f92a5 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/DubboZipkin4AutoConfigurationTests.java
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/DubboZipkin4AutoConfigurationTests.java
@@ -22,6 +22,7 @@
import io.opentelemetry.exporter.zipkin.ZipkinSpanExporter;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.restclient.RestTemplateBuilder;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
@@ -66,6 +67,18 @@ void shouldSupplyRestTemplateSenderWhenUrlConnectionAndWebClientAreUnavailable()
});
}
+ @Test
+ void shouldBackOffRestTemplateSenderWhenRestTemplateBuilderIsUnavailable() {
+ this.contextRunner
+ .withClassLoader(
+ new FilteredClassLoader(URLConnectionSender.class, WebClient.class, RestTemplateBuilder.class))
+ .run((context) -> {
+ assertThat(context).hasNotFailed();
+ assertThat(context).doesNotHaveBean(Sender.class);
+ assertThat(context).hasSingleBean(ZipkinSpanExporter.class);
+ });
+ }
+
@Configuration(proxyBeanMethods = false)
private static class NoOpReporterConfiguration {
From 1e7425dad3f3be8e78e70974d8cb3a505f096726 Mon Sep 17 00:00:00 2001
From: SavitarC <71271200+savitarc@users.noreply.github.com>
Date: Sun, 17 May 2026 12:40:47 +0800
Subject: [PATCH 4/8] feat(spring-boot): Add Boot 4 API compatibility paths
---
.../logger/slf4j/Slf4jLoggerAdapter.java | 9 +
.../utils/ConcurrentHashMapUtilsTest.java | 8 +-
.../dubbo/common/utils/NetUtilsTest.java | 4 +-
.../metadata/rest/SpringRestService.java | 4 +-
.../DubboDeployApplicationListener.java | 2 +-
dubbo-plugin/dubbo-rest-spring/pom.xml | 60 +++++
.../spring/HandlerInterceptorAdapter.java | 110 +++++++++
.../spring/SpringMiscArgumentResolver.java | 70 ++++++
.../spring/SpringResponseRestFilter.java | 195 ++++++++++++++++
.../support/spring/SpringRestToolKit.java | 176 +++++++++++++++
.../spring/HandlerInterceptorAdapter.java | 0
.../spring/SpringMiscArgumentResolver.java | 0
.../spring/SpringResponseRestFilter.java | 0
.../support/spring/SpringRestToolKit.java | 0
.../pom.xml | 24 ++
.../CompatibleHealthIndicatorCondition.java | 33 +++
...ubboEndpointMetadataAutoConfiguration.java | 6 +-
...bboExtensionEndpointAutoConfiguration.java | 4 +-
...DubboHealthIndicatorAutoConfiguration.java | 6 +-
.../DubboMetricsAutoConfiguration.java | 7 +-
.../health/DubboHealthIndicatorTest.java | 6 +-
.../dubbo-spring-boot-actuator/pom.xml | 63 ++++++
.../DubboHealthIndicatorProperties.java | 4 +-
.../actuate/health/DubboHealthIndicator.java | 213 ++++++++++++++++++
.../actuate/health/DubboHealthIndicator.java | 0
.../dubbo-spring-boot-autoconfigure/pom.xml | 87 +++++++
.../dubbo-spring-boot/pom.xml | 63 ++++++
...ultPropertiesEnvironmentPostProcessor.java | 1 +
.../DubboTagCookieInterceptor.java | 55 +++++
.../DubboTagHeaderOrParameterInterceptor.java | 47 ++++
.../DubboTagCookieInterceptor.java | 0
.../DubboTagHeaderOrParameterInterceptor.java | 0
.../test/resources/META-INF/dubbo.properties | 1 +
.../org/apache/dubbo/dependency/FileTest.java | 71 +++++-
pom.xml | 181 +++++++++++++++
35 files changed, 1481 insertions(+), 29 deletions(-)
create mode 100644 dubbo-plugin/dubbo-rest-spring/src/spring-boot-4/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/HandlerInterceptorAdapter.java
create mode 100644 dubbo-plugin/dubbo-rest-spring/src/spring-boot-4/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringMiscArgumentResolver.java
create mode 100644 dubbo-plugin/dubbo-rest-spring/src/spring-boot-4/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringResponseRestFilter.java
create mode 100644 dubbo-plugin/dubbo-rest-spring/src/spring-boot-4/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringRestToolKit.java
rename dubbo-plugin/dubbo-rest-spring/src/{main => spring-boot-before-4}/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/HandlerInterceptorAdapter.java (100%)
rename dubbo-plugin/dubbo-rest-spring/src/{main => spring-boot-before-4}/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringMiscArgumentResolver.java (100%)
rename dubbo-plugin/dubbo-rest-spring/src/{main => spring-boot-before-4}/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringResponseRestFilter.java (100%)
rename dubbo-plugin/dubbo-rest-spring/src/{main => spring-boot-before-4}/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringRestToolKit.java (100%)
create mode 100644 dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/CompatibleHealthIndicatorCondition.java
create mode 100644 dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/spring-boot-4/java/org/apache/dubbo/spring/boot/actuate/health/DubboHealthIndicator.java
rename dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/{main => spring-boot-before-4}/java/org/apache/dubbo/spring/boot/actuate/health/DubboHealthIndicator.java (100%)
create mode 100644 dubbo-spring-boot-project/dubbo-spring-boot/src/spring-boot-4/java/org/apache/dubbo/spring/boot/interceptor/DubboTagCookieInterceptor.java
create mode 100644 dubbo-spring-boot-project/dubbo-spring-boot/src/spring-boot-4/java/org/apache/dubbo/spring/boot/interceptor/DubboTagHeaderOrParameterInterceptor.java
rename dubbo-spring-boot-project/dubbo-spring-boot/src/{main => spring-boot-before-4}/java/org/apache/dubbo/spring/boot/interceptor/DubboTagCookieInterceptor.java (100%)
rename dubbo-spring-boot-project/dubbo-spring-boot/src/{main => spring-boot-before-4}/java/org/apache/dubbo/spring/boot/interceptor/DubboTagHeaderOrParameterInterceptor.java (100%)
create mode 100644 dubbo-spring-boot-project/dubbo-spring-boot/src/test/resources/META-INF/dubbo.properties
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/logger/slf4j/Slf4jLoggerAdapter.java b/dubbo-common/src/main/java/org/apache/dubbo/common/logger/slf4j/Slf4jLoggerAdapter.java
index a0e35d1bfd5a..f801e3b257b1 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/logger/slf4j/Slf4jLoggerAdapter.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/logger/slf4j/Slf4jLoggerAdapter.java
@@ -27,6 +27,7 @@
public class Slf4jLoggerAdapter implements LoggerAdapter {
public static final String NAME = "slf4j";
+ private static final String NOP_LOGGER_FACTORY = "org.slf4j.helpers.NOPLoggerFactory";
private Level level;
private File file;
@@ -82,6 +83,14 @@ public void setFile(File file) {
@Override
public boolean isConfigured() {
+ try {
+ Object loggerFactory = LoggerFactory.getILoggerFactory();
+ return loggerFactory != null
+ && !NOP_LOGGER_FACTORY.equals(loggerFactory.getClass().getName());
+ } catch (Throwable ignore) {
+ // Fallback for unusual SLF4J initialization failures.
+ }
+
try {
ClassUtils.forName("org.slf4j.impl.StaticLoggerBinder");
return true;
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/ConcurrentHashMapUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/ConcurrentHashMapUtilsTest.java
index 45ce4e1b09cd..fd769af189f1 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/ConcurrentHashMapUtilsTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/ConcurrentHashMapUtilsTest.java
@@ -19,7 +19,9 @@
import java.util.concurrent.ConcurrentHashMap;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledForJreRange;
+import org.junit.jupiter.api.condition.DisabledOnJre;
+import org.junit.jupiter.api.condition.EnabledOnJre;
+import org.junit.jupiter.api.condition.JRE;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -39,7 +41,7 @@ public void testComputeIfAbsent() {
}
@Test
- @EnabledForJreRange(max = org.junit.jupiter.api.condition.JRE.JAVA_8)
+ @EnabledOnJre(JRE.JAVA_8)
public void issue11986ForJava8Test() {
// https://github.com/apache/dubbo/issues/11986
final ConcurrentHashMap map = new ConcurrentHashMap<>();
@@ -54,7 +56,7 @@ public void issue11986ForJava8Test() {
}
@Test
- @EnabledForJreRange(min = org.junit.jupiter.api.condition.JRE.JAVA_9)
+ @DisabledOnJre(JRE.JAVA_8)
public void issue11986ForJava17Test() {
// https://github.com/apache/dubbo/issues/11986
final ConcurrentHashMap map = new ConcurrentHashMap<>();
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/NetUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/NetUtilsTest.java
index 9f093229ab0e..d80c56100875 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/NetUtilsTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/NetUtilsTest.java
@@ -336,8 +336,8 @@ void testMatchIpRangeMatchWhenIpv6Exception() {
@Test
void testMatchIpRangeMatchWhenIpWrongException() {
UnknownHostException thrown = assertThrows(
- UnknownHostException.class, () -> NetUtils.matchIpRange("192.168.1.63", "192.168.1.ff", 90));
- assertTrue(thrown.getMessage().contains("192.168.1.ff"));
+ UnknownHostException.class, () -> NetUtils.matchIpRange("192.168.1.63", "192.168.1.256", 90));
+ assertTrue(thrown.getMessage().contains("192.168.1.256"));
}
@Test
diff --git a/dubbo-compatible/src/test/java/org/apache/dubbo/metadata/rest/SpringRestService.java b/dubbo-compatible/src/test/java/org/apache/dubbo/metadata/rest/SpringRestService.java
index b475bd53da82..ebe2c5e6fceb 100644
--- a/dubbo-compatible/src/test/java/org/apache/dubbo/metadata/rest/SpringRestService.java
+++ b/dubbo-compatible/src/test/java/org/apache/dubbo/metadata/rest/SpringRestService.java
@@ -75,7 +75,7 @@ public String form(@RequestParam("f") String form) {
}
@Override
- @PostMapping(value = "/request/body/map", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+ @PostMapping(value = "/request/body/map", produces = MediaType.APPLICATION_JSON_VALUE)
public User requestBodyMap(@RequestBody Map data, @RequestParam("param") String param) {
User user = new User();
user.setId(((Integer) data.get("id")).longValue());
@@ -84,7 +84,7 @@ public User requestBodyMap(@RequestBody Map data, @RequestParam(
return user;
}
- @PostMapping(value = "/request/body/user", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
+ @PostMapping(value = "/request/body/user", consumes = MediaType.APPLICATION_JSON_VALUE)
@Override
public Map requestBodyUser(@RequestBody User user) {
Map map = new HashMap<>();
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboDeployApplicationListener.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboDeployApplicationListener.java
index a52246bf9088..186827d5beb1 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboDeployApplicationListener.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboDeployApplicationListener.java
@@ -148,7 +148,7 @@ private void publishModuleEvent(DeployState state, Throwable cause) {
@Override
public void onApplicationEvent(ApplicationContextEvent event) {
- if (nullSafeEquals(applicationContext, event.getSource())) {
+ if (nullSafeEquals(applicationContext, event.getApplicationContext())) {
if (event instanceof ContextRefreshedEvent) {
onContextRefreshedEvent((ContextRefreshedEvent) event);
} else if (event instanceof ContextClosedEvent) {
diff --git a/dubbo-plugin/dubbo-rest-spring/pom.xml b/dubbo-plugin/dubbo-rest-spring/pom.xml
index c786df8317b0..ed3ca7f7293b 100644
--- a/dubbo-plugin/dubbo-rest-spring/pom.xml
+++ b/dubbo-plugin/dubbo-rest-spring/pom.xml
@@ -117,6 +117,66 @@
+
+ spring-boot-before-4
+
+ true
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+
+
+ add-spring-boot-before-4-sources
+
+ add-source
+
+ generate-sources
+
+
+ src/spring-boot-before-4/java
+
+
+
+
+
+
+
+
+
+ spring-boot-4
+
+
+ jakarta.servlet
+ jakarta.servlet-api
+ provided
+
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+
+
+ add-spring-boot-4-sources
+
+ add-source
+
+ generate-sources
+
+
+ src/spring-boot-4/java
+
+
+
+
+
+
+
+
rpc-rest-test
diff --git a/dubbo-plugin/dubbo-rest-spring/src/spring-boot-4/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/HandlerInterceptorAdapter.java b/dubbo-plugin/dubbo-rest-spring/src/spring-boot-4/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/HandlerInterceptorAdapter.java
new file mode 100644
index 000000000000..b50932ee393c
--- /dev/null
+++ b/dubbo-plugin/dubbo-rest-spring/src/spring-boot-4/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/HandlerInterceptorAdapter.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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.apache.dubbo.rpc.protocol.tri.rest.support.spring;
+
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.remoting.http12.HttpRequest;
+import org.apache.dubbo.remoting.http12.HttpResponse;
+import org.apache.dubbo.rpc.Result;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants;
+import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtensionAdapter;
+import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestFilter;
+import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestFilter.Listener;
+import org.apache.dubbo.rpc.protocol.tri.rest.util.RestUtils;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import java.util.Arrays;
+
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.ModelAndView;
+
+@Activate(onClass = "org.springframework.web.servlet.HandlerInterceptor")
+public final class HandlerInterceptorAdapter implements RestExtensionAdapter {
+ @Override
+ public boolean accept(Object extension) {
+ return extension instanceof HandlerInterceptor;
+ }
+
+ @Override
+ public RestFilter adapt(HandlerInterceptor extension) {
+ return new HandlerInterceptorRestFilter(extension);
+ }
+
+ private static final class HandlerInterceptorRestFilter implements RestFilter, Listener {
+
+ private final HandlerInterceptor interceptor;
+
+ @Override
+ public int getPriority() {
+ return RestUtils.getPriority(interceptor);
+ }
+
+ @Override
+ public String[] getPatterns() {
+ return RestUtils.getPattens(interceptor);
+ }
+
+ public HandlerInterceptorRestFilter(HandlerInterceptor interceptor) {
+ this.interceptor = interceptor;
+ }
+
+ @Override
+ public void doFilter(HttpRequest request, HttpResponse response, FilterChain chain) throws Exception {
+ Object handler = request.attribute(RestConstants.HANDLER_ATTRIBUTE);
+ if (interceptor.preHandle((HttpServletRequest) request, (HttpServletResponse) response, handler)) {
+ chain.doFilter(request, response);
+ }
+ }
+
+ @Override
+ public void onResponse(Result result, HttpRequest request, HttpResponse response) throws Exception {
+ if (result.hasException()) {
+ onError(result.getException(), request, response);
+ return;
+ }
+ Object handler = request.attribute(RestConstants.HANDLER_ATTRIBUTE);
+ ModelAndView mv = new ModelAndView();
+ mv.addObject("result", result);
+ interceptor.postHandle((HttpServletRequest) request, (HttpServletResponse) response, handler, mv);
+ }
+
+ @Override
+ public void onError(Throwable t, HttpRequest request, HttpResponse response) throws Exception {
+ Object handler = request.attribute(RestConstants.HANDLER_ATTRIBUTE);
+ Exception ex = t instanceof Exception ? (Exception) t : new RpcException(t);
+ interceptor.afterCompletion((HttpServletRequest) request, (HttpServletResponse) response, handler, ex);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("RestFilter{interceptor=");
+ sb.append(interceptor);
+ int priority = getPriority();
+ if (priority != 0) {
+ sb.append(", priority=").append(priority);
+ }
+ String[] patterns = getPatterns();
+ if (patterns != null) {
+ sb.append(", patterns=").append(Arrays.toString(patterns));
+ }
+ return sb.append('}').toString();
+ }
+ }
+}
diff --git a/dubbo-plugin/dubbo-rest-spring/src/spring-boot-4/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringMiscArgumentResolver.java b/dubbo-plugin/dubbo-rest-spring/src/spring-boot-4/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringMiscArgumentResolver.java
new file mode 100644
index 000000000000..c9d641cc72fc
--- /dev/null
+++ b/dubbo-plugin/dubbo-rest-spring/src/spring-boot-4/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringMiscArgumentResolver.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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.apache.dubbo.rpc.protocol.tri.rest.support.spring;
+
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.remoting.http12.HttpRequest;
+import org.apache.dubbo.remoting.http12.HttpResponse;
+import org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta;
+
+import jakarta.servlet.http.HttpServletRequest;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.util.CollectionUtils;
+import org.springframework.web.context.request.NativeWebRequest;
+import org.springframework.web.context.request.ServletWebRequest;
+import org.springframework.web.context.request.WebRequest;
+
+@Activate(onClass = "org.springframework.web.context.request.WebRequest")
+public class SpringMiscArgumentResolver implements ArgumentResolver {
+
+ private static final Set> SUPPORTED_TYPES = new HashSet<>();
+
+ static {
+ SUPPORTED_TYPES.add(WebRequest.class);
+ SUPPORTED_TYPES.add(NativeWebRequest.class);
+ SUPPORTED_TYPES.add(HttpEntity.class);
+ SUPPORTED_TYPES.add(HttpHeaders.class);
+ }
+
+ @Override
+ public boolean accept(ParameterMeta parameter) {
+ return SUPPORTED_TYPES.contains(parameter.getActualType());
+ }
+
+ @Override
+ public Object resolve(ParameterMeta parameter, HttpRequest request, HttpResponse response) {
+ Class> type = parameter.getActualType();
+ if (type == WebRequest.class || type == NativeWebRequest.class) {
+ return new ServletWebRequest((HttpServletRequest) request);
+ }
+ if (type == HttpEntity.class) {
+ return new HttpEntity<>(
+ CollectionUtils.toMultiValueMap(request.headers().asMap()));
+ }
+ if (type == HttpHeaders.class) {
+ return new HttpHeaders(
+ CollectionUtils.toMultiValueMap(request.headers().asMap()));
+ }
+ return null;
+ }
+}
diff --git a/dubbo-plugin/dubbo-rest-spring/src/spring-boot-4/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringResponseRestFilter.java b/dubbo-plugin/dubbo-rest-spring/src/spring-boot-4/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringResponseRestFilter.java
new file mode 100644
index 000000000000..fb03ecb85133
--- /dev/null
+++ b/dubbo-plugin/dubbo-rest-spring/src/spring-boot-4/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringResponseRestFilter.java
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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.apache.dubbo.rpc.protocol.tri.rest.support.spring;
+
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.common.utils.Pair;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.remoting.http12.HttpRequest;
+import org.apache.dubbo.remoting.http12.HttpResponse;
+import org.apache.dubbo.remoting.http12.HttpResult;
+import org.apache.dubbo.rpc.Result;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants;
+import org.apache.dubbo.rpc.protocol.tri.rest.RestException;
+import org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver;
+import org.apache.dubbo.rpc.protocol.tri.rest.argument.CompositeArgumentResolver;
+import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestFilter;
+import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestFilter.Listener;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMapping;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.HandlerMeta;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.MethodMeta;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ResponseMeta;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ServiceMeta;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@Activate(order = -10000, onClass = "org.springframework.http.ResponseEntity")
+public class SpringResponseRestFilter implements RestFilter, Listener {
+
+ private final ArgumentResolver argumentResolver;
+ private final Map> cache = CollectionUtils.newConcurrentHashMap();
+
+ public SpringResponseRestFilter(FrameworkModel frameworkModel) {
+ argumentResolver = frameworkModel.getOrRegisterBean(CompositeArgumentResolver.class);
+ }
+
+ @Override
+ public void onResponse(Result result, HttpRequest request, HttpResponse response) {
+ if (result.hasException()) {
+ HandlerMeta handler = request.attribute(RestConstants.HANDLER_ATTRIBUTE);
+ if (handler == null) {
+ return;
+ }
+
+ Throwable t = result.getException();
+ Key key = new Key(handler.getMethod().getMethod(), t.getClass());
+ cache.computeIfAbsent(key, k -> findSuitableExceptionHandler(handler.getService(), k.type))
+ .ifPresent(m -> {
+ try {
+ result.setValue(invokeExceptionHandler(m, t, request, response));
+ result.setException(null);
+ } catch (Throwable th) {
+ result.setException(th);
+ }
+ });
+ return;
+ }
+
+ Object value = result.getValue();
+ if (value instanceof ResponseEntity) {
+ ResponseEntity> entity = (ResponseEntity>) value;
+ result.setValue(HttpResult.builder()
+ .body(entity.getBody())
+ .status(Helper.getStatusCode(entity))
+ .headers(entity.getHeaders().asMultiValueMap())
+ .build());
+ return;
+ }
+
+ RequestMapping mapping = request.attribute(RestConstants.MAPPING_ATTRIBUTE);
+ if (mapping == null) {
+ return;
+ }
+ ResponseMeta responseMeta = mapping.getResponse();
+ if (responseMeta == null) {
+ return;
+ }
+ String reason = responseMeta.getReason();
+ result.setValue(HttpResult.builder()
+ .body(reason == null ? result.getValue() : reason)
+ .status(responseMeta.getStatus())
+ .build());
+ }
+
+ private Optional findSuitableExceptionHandler(ServiceMeta serviceMeta, Class> exType) {
+ if (serviceMeta.getExceptionHandlers() == null) {
+ return Optional.empty();
+ }
+ List, MethodMeta>> candidates = new ArrayList<>();
+ for (MethodMeta methodMeta : serviceMeta.getExceptionHandlers()) {
+ ExceptionHandler anno = methodMeta.getMethod().getAnnotation(ExceptionHandler.class);
+ if (anno == null) {
+ continue;
+ }
+ for (Class> type : anno.value()) {
+ if (type.isAssignableFrom(exType)) {
+ candidates.add(Pair.of(type, methodMeta));
+ }
+ }
+ }
+ int size = candidates.size();
+ if (size == 0) {
+ return Optional.empty();
+ }
+ if (size > 1) {
+ candidates.sort((o1, o2) -> {
+ Class> c1 = o1.getLeft();
+ Class> c2 = o2.getLeft();
+ if (c1.equals(c2)) {
+ return 0;
+ }
+ return c1.isAssignableFrom(c2) ? 1 : -1;
+ });
+ }
+ return Optional.of(candidates.get(0).getRight());
+ }
+
+ private Object invokeExceptionHandler(MethodMeta meta, Throwable t, HttpRequest request, HttpResponse response) {
+ ParameterMeta[] parameters = meta.getParameters();
+ int len = parameters.length;
+ Object[] args = new Object[len];
+ for (int i = 0; i < len; i++) {
+ ParameterMeta parameter = parameters[i];
+ if (parameter.getType().isInstance(t)) {
+ args[i] = t;
+ } else {
+ args[i] = argumentResolver.resolve(parameter, request, response);
+ }
+ }
+ Object value;
+ try {
+ value = meta.getMethod().invoke(meta.getServiceMeta().getService(), args);
+ } catch (Exception e) {
+ throw new RestException("Failed to invoke exception handler method: " + meta.getMethod(), e);
+ }
+ AnnotationMeta> rs = meta.getAnnotation(ResponseStatus.class);
+ if (rs == null) {
+ return value;
+ }
+ HttpStatus status = rs.getEnum("value");
+ String reason = rs.getString("reason");
+ return HttpResult.builder()
+ .body(StringUtils.isEmpty(reason) ? value : reason)
+ .status(status.value())
+ .build();
+ }
+
+ private static final class Key {
+ private final Method method;
+ private final Class> type;
+
+ Key(Method method, Class> type) {
+ this.method = method;
+ this.type = type;
+ }
+
+ @Override
+ @SuppressWarnings({"EqualsWhichDoesntCheckParameterClass", "EqualsDoesntCheckParameterClass"})
+ public boolean equals(Object obj) {
+ Key other = (Key) obj;
+ return method.equals(other.method) && type.equals(other.type);
+ }
+
+ @Override
+ public int hashCode() {
+ return method.hashCode() * 31 + type.hashCode();
+ }
+ }
+}
diff --git a/dubbo-plugin/dubbo-rest-spring/src/spring-boot-4/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringRestToolKit.java b/dubbo-plugin/dubbo-rest-spring/src/spring-boot-4/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringRestToolKit.java
new file mode 100644
index 000000000000..9c7e1baac0f7
--- /dev/null
+++ b/dubbo-plugin/dubbo-rest-spring/src/spring-boot-4/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringRestToolKit.java
@@ -0,0 +1,176 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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.apache.dubbo.rpc.protocol.tri.rest.support.spring;
+
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.common.utils.DefaultParameterNameReader;
+import org.apache.dubbo.common.utils.ParameterNameReader;
+import org.apache.dubbo.config.spring.extension.SpringExtensionInjector;
+import org.apache.dubbo.remoting.http12.HttpRequest;
+import org.apache.dubbo.remoting.http12.HttpResponse;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.protocol.tri.rest.Messages;
+import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants;
+import org.apache.dubbo.rpc.protocol.tri.rest.RestException;
+import org.apache.dubbo.rpc.protocol.tri.rest.argument.CompositeArgumentResolver;
+import org.apache.dubbo.rpc.protocol.tri.rest.argument.GeneralTypeConverter;
+import org.apache.dubbo.rpc.protocol.tri.rest.argument.TypeConverter;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.MethodParameterMeta;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta;
+import org.apache.dubbo.rpc.protocol.tri.rest.util.RestToolKit;
+import org.apache.dubbo.rpc.protocol.tri.rest.util.RestUtils;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.Map;
+
+import org.springframework.beans.factory.config.ConfigurableBeanFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.core.MethodParameter;
+import org.springframework.core.annotation.AnnotatedElementUtils;
+import org.springframework.core.convert.ConversionService;
+import org.springframework.core.convert.TypeDescriptor;
+import org.springframework.core.convert.support.DefaultConversionService;
+import org.springframework.util.PropertyPlaceholderHelper;
+
+final class SpringRestToolKit implements RestToolKit {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(SpringRestToolKit.class);
+
+ private final Map cache = CollectionUtils.newConcurrentHashMap();
+ private final ConfigurableBeanFactory beanFactory;
+ private final PropertyPlaceholderHelper placeholderHelper;
+ private final ConfigurationWrapper configuration;
+ private final ConversionService conversionService;
+ private final TypeConverter typeConverter;
+ private final BeanArgumentBinder argumentBinder;
+ private final ParameterNameReader parameterNameReader;
+ private final CompositeArgumentResolver argumentResolver;
+
+ public SpringRestToolKit(FrameworkModel frameworkModel) {
+ ApplicationModel applicationModel = frameworkModel.defaultApplication();
+ SpringExtensionInjector injector = SpringExtensionInjector.get(applicationModel);
+ ApplicationContext context = injector.getContext();
+ if (context instanceof ConfigurableApplicationContext) {
+ beanFactory = ((ConfigurableApplicationContext) context).getBeanFactory();
+ placeholderHelper = null;
+ configuration = null;
+ } else {
+ beanFactory = null;
+ placeholderHelper = new PropertyPlaceholderHelper("${", "}", ":", null, true);
+ configuration = new ConfigurationWrapper(applicationModel);
+ }
+ if (context != null && context.containsBean("mvcConversionService")) {
+ conversionService = context.getBean("mvcConversionService", ConversionService.class);
+ } else {
+ conversionService = DefaultConversionService.getSharedInstance();
+ }
+ typeConverter = frameworkModel.getOrRegisterBean(GeneralTypeConverter.class);
+ parameterNameReader = frameworkModel.getOrRegisterBean(DefaultParameterNameReader.class);
+ argumentResolver = frameworkModel.getOrRegisterBean(CompositeArgumentResolver.class);
+ argumentBinder = new BeanArgumentBinder(argumentResolver, conversionService);
+ }
+
+ @Override
+ public int getDialect() {
+ return RestConstants.DIALECT_SPRING_MVC;
+ }
+
+ @Override
+ public String resolvePlaceholders(String text) {
+ if (!RestUtils.hasPlaceholder(text)) {
+ return text;
+ }
+ if (beanFactory != null) {
+ return beanFactory.resolveEmbeddedValue(text);
+ }
+ return placeholderHelper.replacePlaceholders(text, configuration);
+ }
+
+ @Override
+ public Object convert(Object value, ParameterMeta parameter) {
+ boolean tried = false;
+ if (value instanceof Collection || value instanceof Map) {
+ tried = true;
+ Object target = typeConverter.convert(value, parameter.getGenericType());
+ if (target != null) {
+ return target;
+ }
+ }
+ if (parameter instanceof MethodParameterMeta) {
+ TypeDescriptor targetType = cache.computeIfAbsent(
+ (MethodParameterMeta) parameter,
+ k -> new TypeDescriptor(new MethodParameter(k.getMethod(), k.getIndex())));
+ TypeDescriptor sourceType = TypeDescriptor.forObject(value);
+ if (conversionService.canConvert(sourceType, targetType)) {
+ try {
+ return conversionService.convert(value, sourceType, targetType);
+ } catch (Throwable t) {
+ LOGGER.debug(
+ "Spring convert value '{}' from type [{}] to type [{}] failed",
+ value,
+ value.getClass(),
+ parameter.getGenericType(),
+ t);
+ }
+ }
+ }
+ Object target = tried ? null : typeConverter.convert(value, parameter.getGenericType());
+ if (target == null && value != null) {
+ throw new RestException(
+ Messages.ARGUMENT_CONVERT_ERROR,
+ parameter.getName(),
+ value,
+ value.getClass(),
+ parameter.getGenericType());
+ }
+ return target;
+ }
+
+ @Override
+ public Object bind(ParameterMeta parameter, HttpRequest request, HttpResponse response) {
+ return argumentBinder.bind(parameter, request, response);
+ }
+
+ @Override
+ public NamedValueMeta getNamedValueMeta(ParameterMeta parameter) {
+ return argumentResolver.getNamedValueMeta(parameter);
+ }
+
+ @Override
+ public String[] getParameterNames(Method method) {
+ return parameterNameReader.readParameterNames(method);
+ }
+
+ @Override
+ public String[] getParameterNames(Constructor> ctor) {
+ return parameterNameReader.readParameterNames(ctor);
+ }
+
+ @Override
+ public Map getAttributes(AnnotatedElement element, Annotation annotation) {
+ return AnnotatedElementUtils.getMergedAnnotationAttributes(element, annotation.annotationType());
+ }
+}
diff --git a/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/HandlerInterceptorAdapter.java b/dubbo-plugin/dubbo-rest-spring/src/spring-boot-before-4/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/HandlerInterceptorAdapter.java
similarity index 100%
rename from dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/HandlerInterceptorAdapter.java
rename to dubbo-plugin/dubbo-rest-spring/src/spring-boot-before-4/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/HandlerInterceptorAdapter.java
diff --git a/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringMiscArgumentResolver.java b/dubbo-plugin/dubbo-rest-spring/src/spring-boot-before-4/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringMiscArgumentResolver.java
similarity index 100%
rename from dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringMiscArgumentResolver.java
rename to dubbo-plugin/dubbo-rest-spring/src/spring-boot-before-4/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringMiscArgumentResolver.java
diff --git a/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringResponseRestFilter.java b/dubbo-plugin/dubbo-rest-spring/src/spring-boot-before-4/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringResponseRestFilter.java
similarity index 100%
rename from dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringResponseRestFilter.java
rename to dubbo-plugin/dubbo-rest-spring/src/spring-boot-before-4/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringResponseRestFilter.java
diff --git a/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringRestToolKit.java b/dubbo-plugin/dubbo-rest-spring/src/spring-boot-before-4/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringRestToolKit.java
similarity index 100%
rename from dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringRestToolKit.java
rename to dubbo-plugin/dubbo-rest-spring/src/spring-boot-before-4/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringRestToolKit.java
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/pom.xml b/dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/pom.xml
index 27d7f6f39e79..be6cdc322312 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/pom.xml
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/pom.xml
@@ -112,12 +112,23 @@
org.springframework.boot
spring-boot-starter-test
test
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
+
org.springframework.boot
spring-boot-starter-log4j2
test
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ test
+
net.bytebuddy
@@ -141,4 +152,17 @@
test
+
+
+
+ spring-boot-4
+
+
+ org.springframework.boot
+ spring-boot-health
+ test
+
+
+
+
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/CompatibleHealthIndicatorCondition.java b/dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/CompatibleHealthIndicatorCondition.java
new file mode 100644
index 000000000000..0dbbf1bf50ac
--- /dev/null
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/CompatibleHealthIndicatorCondition.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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.apache.dubbo.spring.boot.actuate.autoconfigure;
+
+import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+
+class CompatibleHealthIndicatorCondition extends AnyNestedCondition {
+
+ CompatibleHealthIndicatorCondition() {
+ super(ConfigurationPhase.PARSE_CONFIGURATION);
+ }
+
+ @ConditionalOnClass(name = "org.springframework.boot.actuate.health.HealthIndicator")
+ static class SpringBootBefore4HealthIndicator {}
+
+ @ConditionalOnClass(name = "org.springframework.boot.health.contributor.HealthIndicator")
+ static class SpringBoot4HealthIndicator {}
+}
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboEndpointMetadataAutoConfiguration.java b/dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboEndpointMetadataAutoConfiguration.java
index a4b2a101ba8b..51047a1b7c1b 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboEndpointMetadataAutoConfiguration.java
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboEndpointMetadataAutoConfiguration.java
@@ -19,9 +19,9 @@
import org.apache.dubbo.spring.boot.actuate.endpoint.metadata.AbstractDubboMetadata;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_PREFIX;
@@ -30,9 +30,7 @@
* Dubbo Endpoints Metadata Auto-{@link Configuration}
*/
@ConditionalOnProperty(prefix = DUBBO_PREFIX, name = "enabled", matchIfMissing = true)
-@ConditionalOnClass(
- name = {"org.springframework.boot.actuate.health.Health" // If spring-boot-actuator is present
- })
+@Conditional(CompatibleHealthIndicatorCondition.class)
@Configuration
@AutoConfigureAfter(
name = {
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboExtensionEndpointAutoConfiguration.java b/dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboExtensionEndpointAutoConfiguration.java
index 87042c122cf4..76317ba23620 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboExtensionEndpointAutoConfiguration.java
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboExtensionEndpointAutoConfiguration.java
@@ -19,9 +19,9 @@
import org.apache.dubbo.spring.boot.actuate.endpoint.DubboActuatorProperties;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_PREFIX;
@@ -30,7 +30,7 @@
* Dubbo Extension Endpoints Auto-{@link Configuration}
*/
@ConditionalOnProperty(prefix = DUBBO_PREFIX, name = "enabled", matchIfMissing = true)
-@ConditionalOnClass(name = {"org.springframework.boot.actuate.health.Health"})
+@Conditional(CompatibleHealthIndicatorCondition.class)
@Configuration
@AutoConfigureAfter(
name = {"org.apache.dubbo.spring.boot.actuate.autoconfigure.DubboEndpointMetadataAutoConfiguration"})
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboHealthIndicatorAutoConfiguration.java b/dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboHealthIndicatorAutoConfiguration.java
index 50d31bc00a32..01bb152d56fa 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboHealthIndicatorAutoConfiguration.java
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboHealthIndicatorAutoConfiguration.java
@@ -19,22 +19,20 @@
import org.apache.dubbo.spring.boot.actuate.health.DubboHealthIndicator;
import org.apache.dubbo.spring.boot.actuate.health.DubboHealthIndicatorProperties;
-import org.springframework.boot.actuate.health.HealthIndicator;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
/**
* Dubbo {@link DubboHealthIndicator} Auto Configuration
*
- * @see HealthIndicator
* @since 2.7.0
*/
@Configuration
-@ConditionalOnClass(name = {"org.springframework.boot.actuate.health.Health"})
+@Conditional(CompatibleHealthIndicatorCondition.class)
@ConditionalOnProperty(
name = {"management.health.dubbo.enabled", "dubbo.enabled"},
matchIfMissing = true,
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboMetricsAutoConfiguration.java b/dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboMetricsAutoConfiguration.java
index ee518190d300..5c9e532aa468 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboMetricsAutoConfiguration.java
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboMetricsAutoConfiguration.java
@@ -19,7 +19,6 @@
import org.apache.dubbo.spring.boot.actuate.mertics.DubboMetricsBinder;
import io.micrometer.core.instrument.MeterRegistry;
-import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -33,7 +32,11 @@
@ConditionalOnProperty(prefix = DUBBO_PREFIX, name = "enabled", matchIfMissing = true)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication
-@AutoConfigureAfter(CompositeMeterRegistryAutoConfiguration.class)
+@AutoConfigureAfter(
+ name = {
+ "org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration",
+ "org.springframework.boot.micrometer.metrics.autoconfigure.CompositeMeterRegistryAutoConfiguration"
+ })
public class DubboMetricsAutoConfiguration {
@Bean
@ConditionalOnBean({MeterRegistry.class})
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/actuate/health/DubboHealthIndicatorTest.java b/dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/actuate/health/DubboHealthIndicatorTest.java
index 3509ebde1cf9..3ed23229cf9b 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/actuate/health/DubboHealthIndicatorTest.java
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/actuate/health/DubboHealthIndicatorTest.java
@@ -23,8 +23,6 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.actuate.health.Health;
-import org.springframework.boot.actuate.health.Status;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;
@@ -77,8 +75,6 @@ void testResolveStatusCheckerNamesMap() {
@Test
void testHealth() {
- Health health = dubboHealthIndicator.health();
-
- assertEquals(Status.UNKNOWN, health.getStatus());
+ assertEquals("UNKNOWN", dubboHealthIndicator.health().getStatus().toString());
}
}
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-actuator/pom.xml b/dubbo-spring-boot-project/dubbo-spring-boot-actuator/pom.xml
index d579624b0311..17358c4db740 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-actuator/pom.xml
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-actuator/pom.xml
@@ -125,4 +125,67 @@
+
+
+ spring-boot-before-4
+
+ true
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+
+
+ add-spring-boot-before-4-sources
+
+ add-source
+
+ generate-sources
+
+
+ src/spring-boot-before-4/java
+
+
+
+
+
+
+
+
+
+ spring-boot-4
+
+
+ org.springframework.boot
+ spring-boot-health
+ true
+
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+
+
+ add-spring-boot-4-sources
+
+ add-source
+
+ generate-sources
+
+
+ src/spring-boot-4/java
+
+
+
+
+
+
+
+
+
+
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/health/DubboHealthIndicatorProperties.java b/dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/health/DubboHealthIndicatorProperties.java
index b0ba917dd3af..5f8951528fba 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/health/DubboHealthIndicatorProperties.java
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/health/DubboHealthIndicatorProperties.java
@@ -22,15 +22,13 @@
import java.util.LinkedHashSet;
import java.util.Set;
-import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.context.properties.ConfigurationProperties;
import static org.apache.dubbo.spring.boot.actuate.health.DubboHealthIndicatorProperties.PREFIX;
/**
- * Dubbo {@link HealthIndicator} Properties
+ * Dubbo health indicator properties.
*
- * @see HealthIndicator
* @since 2.7.0
*/
@ConfigurationProperties(prefix = PREFIX, ignoreUnknownFields = false)
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/spring-boot-4/java/org/apache/dubbo/spring/boot/actuate/health/DubboHealthIndicator.java b/dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/spring-boot-4/java/org/apache/dubbo/spring/boot/actuate/health/DubboHealthIndicator.java
new file mode 100644
index 000000000000..6846062e38a9
--- /dev/null
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/spring-boot-4/java/org/apache/dubbo/spring/boot/actuate/health/DubboHealthIndicator.java
@@ -0,0 +1,213 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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.apache.dubbo.spring.boot.actuate.health;
+
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.common.status.StatusChecker;
+import org.apache.dubbo.config.ProtocolConfig;
+import org.apache.dubbo.config.ProviderConfig;
+import org.apache.dubbo.config.context.ConfigManager;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.ModuleModel;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.health.contributor.AbstractHealthIndicator;
+import org.springframework.boot.health.contributor.Health;
+import org.springframework.boot.health.contributor.HealthIndicator;
+import org.springframework.util.StringUtils;
+
+/**
+ * Dubbo {@link HealthIndicator}
+ *
+ * @see HealthIndicator
+ * @since 2.7.0
+ */
+public class DubboHealthIndicator extends AbstractHealthIndicator {
+
+ @Autowired
+ private DubboHealthIndicatorProperties dubboHealthIndicatorProperties;
+
+ // @Autowired(required = false)
+ private Map protocolConfigs = Collections.emptyMap();
+
+ // @Autowired(required = false)
+ private Map providerConfigs = Collections.emptyMap();
+
+ @Autowired
+ private ConfigManager configManager;
+
+ @Autowired
+ private ApplicationModel applicationModel;
+
+ @Override
+ protected void doHealthCheck(Health.Builder builder) throws Exception {
+
+ ExtensionLoader extensionLoader = applicationModel.getExtensionLoader(StatusChecker.class);
+
+ Map statusCheckerNamesMap = resolveStatusCheckerNamesMap();
+
+ boolean hasError = false;
+
+ boolean hasUnknown = false;
+
+ // Up first
+ builder.up();
+
+ for (Map.Entry entry : statusCheckerNamesMap.entrySet()) {
+
+ String statusCheckerName = entry.getKey();
+
+ String source = entry.getValue();
+
+ StatusChecker checker = extensionLoader.getExtension(statusCheckerName);
+
+ org.apache.dubbo.common.status.Status status = checker.check();
+
+ org.apache.dubbo.common.status.Status.Level level = status.getLevel();
+
+ if (!hasError && level.equals(org.apache.dubbo.common.status.Status.Level.ERROR)) {
+ hasError = true;
+ builder.down();
+ }
+
+ if (!hasError && !hasUnknown && level.equals(org.apache.dubbo.common.status.Status.Level.UNKNOWN)) {
+ hasUnknown = true;
+ builder.unknown();
+ }
+
+ Map detail = new LinkedHashMap<>();
+
+ detail.put("source", source);
+ detail.put("status", status);
+
+ builder.withDetail(statusCheckerName, detail);
+ }
+ }
+
+ /**
+ * Resolves the map of {@link StatusChecker}'s name and its' source.
+ *
+ * @return non-null {@link Map}
+ */
+ protected Map resolveStatusCheckerNamesMap() {
+
+ Map statusCheckerNamesMap = new LinkedHashMap<>();
+
+ statusCheckerNamesMap.putAll(resolveStatusCheckerNamesMapFromDubboHealthIndicatorProperties());
+
+ statusCheckerNamesMap.putAll(resolveStatusCheckerNamesMapFromProtocolConfigs());
+
+ statusCheckerNamesMap.putAll(resolveStatusCheckerNamesMapFromProviderConfig());
+
+ return statusCheckerNamesMap;
+ }
+
+ private Map resolveStatusCheckerNamesMapFromDubboHealthIndicatorProperties() {
+
+ DubboHealthIndicatorProperties.Status status = dubboHealthIndicatorProperties.getStatus();
+
+ Map statusCheckerNamesMap = new LinkedHashMap<>();
+
+ for (String statusName : status.getDefaults()) {
+
+ statusCheckerNamesMap.put(statusName, DubboHealthIndicatorProperties.PREFIX + ".status.defaults");
+ }
+
+ for (String statusName : status.getExtras()) {
+
+ statusCheckerNamesMap.put(statusName, DubboHealthIndicatorProperties.PREFIX + ".status.extras");
+ }
+
+ return statusCheckerNamesMap;
+ }
+
+ private Map resolveStatusCheckerNamesMapFromProtocolConfigs() {
+
+ if (protocolConfigs.isEmpty()) {
+ protocolConfigs = configManager.getConfigsMap(ProtocolConfig.class);
+ }
+
+ Map statusCheckerNamesMap = new LinkedHashMap<>();
+
+ for (Map.Entry entry : protocolConfigs.entrySet()) {
+
+ String beanName = entry.getKey();
+
+ ProtocolConfig protocolConfig = entry.getValue();
+
+ Set statusCheckerNames = getStatusCheckerNames(protocolConfig);
+
+ for (String statusCheckerName : statusCheckerNames) {
+
+ String source = buildSource(beanName, protocolConfig);
+
+ statusCheckerNamesMap.put(statusCheckerName, source);
+ }
+ }
+
+ return statusCheckerNamesMap;
+ }
+
+ private Map resolveStatusCheckerNamesMapFromProviderConfig() {
+
+ if (providerConfigs.isEmpty()) {
+ providerConfigs = new LinkedHashMap<>();
+ for (ModuleModel moduleModel : applicationModel.getModuleModels()) {
+ providerConfigs.putAll(moduleModel.getConfigManager().getConfigsMap(ProviderConfig.class));
+ }
+ }
+
+ Map statusCheckerNamesMap = new LinkedHashMap<>();
+
+ for (Map.Entry entry : providerConfigs.entrySet()) {
+
+ String beanName = entry.getKey();
+
+ ProviderConfig providerConfig = entry.getValue();
+
+ Set statusCheckerNames = getStatusCheckerNames(providerConfig);
+
+ for (String statusCheckerName : statusCheckerNames) {
+
+ String source = buildSource(beanName, providerConfig);
+
+ statusCheckerNamesMap.put(statusCheckerName, source);
+ }
+ }
+
+ return statusCheckerNamesMap;
+ }
+
+ private Set getStatusCheckerNames(ProtocolConfig protocolConfig) {
+ String status = protocolConfig.getStatus();
+ return StringUtils.commaDelimitedListToSet(status);
+ }
+
+ private Set getStatusCheckerNames(ProviderConfig providerConfig) {
+ String status = providerConfig.getStatus();
+ return StringUtils.commaDelimitedListToSet(status);
+ }
+
+ private String buildSource(String beanName, Object bean) {
+ return beanName + "@" + bean.getClass().getSimpleName() + ".getStatus()";
+ }
+}
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/health/DubboHealthIndicator.java b/dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/spring-boot-before-4/java/org/apache/dubbo/spring/boot/actuate/health/DubboHealthIndicator.java
similarity index 100%
rename from dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/health/DubboHealthIndicator.java
rename to dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/spring-boot-before-4/java/org/apache/dubbo/spring/boot/actuate/health/DubboHealthIndicator.java
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/pom.xml b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/pom.xml
index fbb2ad9b0d08..842748f85cc9 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/pom.xml
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/pom.xml
@@ -238,6 +238,12 @@
org.springframework.boot
spring-boot-starter-test
test
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
+
@@ -353,10 +359,91 @@
spring-boot-4
2.7.18
+ 5.3.31
9.0.117
+
+ org.springframework
+ spring-aop
+ ${spring-boot-2.spring.version}
+
+
+ org.springframework
+ spring-aspects
+ ${spring-boot-2.spring.version}
+
+
+ org.springframework
+ spring-beans
+ ${spring-boot-2.spring.version}
+
+
+ org.springframework
+ spring-context
+ ${spring-boot-2.spring.version}
+
+
+ org.springframework
+ spring-context-indexer
+ ${spring-boot-2.spring.version}
+
+
+ org.springframework
+ spring-context-support
+ ${spring-boot-2.spring.version}
+
+
+ org.springframework
+ spring-core
+ ${spring-boot-2.spring.version}
+
+
+ org.springframework
+ spring-expression
+ ${spring-boot-2.spring.version}
+
+
+ org.springframework
+ spring-instrument
+ ${spring-boot-2.spring.version}
+
+
+ org.springframework
+ spring-jdbc
+ ${spring-boot-2.spring.version}
+
+
+ org.springframework
+ spring-test
+ ${spring-boot-2.spring.version}
+
+
+ org.springframework
+ spring-tx
+ ${spring-boot-2.spring.version}
+
+
+ org.springframework
+ spring-web
+ ${spring-boot-2.spring.version}
+
+
+ org.springframework
+ spring-webflux
+ ${spring-boot-2.spring.version}
+
+
+ org.springframework
+ spring-webmvc
+ ${spring-boot-2.spring.version}
+
+
+ org.springframework
+ spring-websocket
+ ${spring-boot-2.spring.version}
+
org.springframework.boot
spring-boot
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot/pom.xml b/dubbo-spring-boot-project/dubbo-spring-boot/pom.xml
index addc5752c157..051358f08fe7 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot/pom.xml
+++ b/dubbo-spring-boot-project/dubbo-spring-boot/pom.xml
@@ -100,4 +100,67 @@
+
+
+ spring-boot-before-4
+
+ true
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+
+
+ add-spring-boot-before-4-sources
+
+ add-source
+
+ generate-sources
+
+
+ src/spring-boot-before-4/java
+
+
+
+
+
+
+
+
+
+ spring-boot-4
+
+
+ jakarta.servlet
+ jakarta.servlet-api
+ provided
+
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+
+
+ add-spring-boot-4-sources
+
+ add-source
+
+ generate-sources
+
+
+ src/spring-boot-4/java
+
+
+
+
+
+
+
+
+
+
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot/src/main/java/org/apache/dubbo/spring/boot/env/DubboDefaultPropertiesEnvironmentPostProcessor.java b/dubbo-spring-boot-project/dubbo-spring-boot/src/main/java/org/apache/dubbo/spring/boot/env/DubboDefaultPropertiesEnvironmentPostProcessor.java
index c2180f6a48e5..dc50c63afad3 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot/src/main/java/org/apache/dubbo/spring/boot/env/DubboDefaultPropertiesEnvironmentPostProcessor.java
+++ b/dubbo-spring-boot-project/dubbo-spring-boot/src/main/java/org/apache/dubbo/spring/boot/env/DubboDefaultPropertiesEnvironmentPostProcessor.java
@@ -41,6 +41,7 @@
* {@link SpringApplication#setDefaultProperties(Properties) Spring Boot default properties} for Dubbo
* as late as possible before {@link ConfigurableApplicationContext#refresh() application context refresh}.
*/
+@SuppressWarnings("removal")
public class DubboDefaultPropertiesEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered {
/**
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot/src/spring-boot-4/java/org/apache/dubbo/spring/boot/interceptor/DubboTagCookieInterceptor.java b/dubbo-spring-boot-project/dubbo-spring-boot/src/spring-boot-4/java/org/apache/dubbo/spring/boot/interceptor/DubboTagCookieInterceptor.java
new file mode 100644
index 000000000000..a701d211977a
--- /dev/null
+++ b/dubbo-spring-boot-project/dubbo-spring-boot/src/spring-boot-4/java/org/apache/dubbo/spring/boot/interceptor/DubboTagCookieInterceptor.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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.apache.dubbo.spring.boot.interceptor;
+
+import org.apache.dubbo.common.constants.CommonConstants;
+import org.apache.dubbo.rpc.RpcContext;
+
+import jakarta.servlet.http.Cookie;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.springframework.web.servlet.HandlerInterceptor;
+
+public class DubboTagCookieInterceptor implements HandlerInterceptor {
+
+ @Override
+ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
+ throws Exception {
+ String tag = getSingleCookieValue(request.getCookies(), CommonConstants.TAG_KEY);
+ RpcContext.getClientAttachment().setAttachment(CommonConstants.TAG_KEY, tag);
+ return true;
+ }
+
+ @Override
+ public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
+ throws Exception {
+ RpcContext.getClientAttachment().removeAttachment(CommonConstants.TAG_KEY);
+ }
+
+ private static String getSingleCookieValue(Cookie[] cookies, String name) {
+ if (cookies == null || cookies.length == 0) {
+ return null;
+ }
+ for (Cookie cookie : cookies) {
+ if (name.equals(cookie.getName())) {
+ return cookie.getValue();
+ }
+ }
+ return null;
+ }
+}
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot/src/spring-boot-4/java/org/apache/dubbo/spring/boot/interceptor/DubboTagHeaderOrParameterInterceptor.java b/dubbo-spring-boot-project/dubbo-spring-boot/src/spring-boot-4/java/org/apache/dubbo/spring/boot/interceptor/DubboTagHeaderOrParameterInterceptor.java
new file mode 100644
index 000000000000..fb2782e80058
--- /dev/null
+++ b/dubbo-spring-boot-project/dubbo-spring-boot/src/spring-boot-4/java/org/apache/dubbo/spring/boot/interceptor/DubboTagHeaderOrParameterInterceptor.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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.apache.dubbo.spring.boot.interceptor;
+
+import org.apache.dubbo.common.constants.CommonConstants;
+import org.apache.dubbo.rpc.RpcContext;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.springframework.web.servlet.HandlerInterceptor;
+
+public class DubboTagHeaderOrParameterInterceptor implements HandlerInterceptor {
+
+ @Override
+ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
+ throws Exception {
+ String tag = request.getHeader(CommonConstants.DUBBO_TAG_HEADER);
+ if (tag == null) {
+ tag = request.getParameter(CommonConstants.TAG_KEY);
+ }
+ if (tag != null) {
+ RpcContext.getClientAttachment().setAttachment(CommonConstants.TAG_KEY, tag);
+ }
+ return true;
+ }
+
+ @Override
+ public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
+ throws Exception {
+ RpcContext.getClientAttachment().removeAttachment(CommonConstants.TAG_KEY);
+ }
+}
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot/src/main/java/org/apache/dubbo/spring/boot/interceptor/DubboTagCookieInterceptor.java b/dubbo-spring-boot-project/dubbo-spring-boot/src/spring-boot-before-4/java/org/apache/dubbo/spring/boot/interceptor/DubboTagCookieInterceptor.java
similarity index 100%
rename from dubbo-spring-boot-project/dubbo-spring-boot/src/main/java/org/apache/dubbo/spring/boot/interceptor/DubboTagCookieInterceptor.java
rename to dubbo-spring-boot-project/dubbo-spring-boot/src/spring-boot-before-4/java/org/apache/dubbo/spring/boot/interceptor/DubboTagCookieInterceptor.java
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot/src/main/java/org/apache/dubbo/spring/boot/interceptor/DubboTagHeaderOrParameterInterceptor.java b/dubbo-spring-boot-project/dubbo-spring-boot/src/spring-boot-before-4/java/org/apache/dubbo/spring/boot/interceptor/DubboTagHeaderOrParameterInterceptor.java
similarity index 100%
rename from dubbo-spring-boot-project/dubbo-spring-boot/src/main/java/org/apache/dubbo/spring/boot/interceptor/DubboTagHeaderOrParameterInterceptor.java
rename to dubbo-spring-boot-project/dubbo-spring-boot/src/spring-boot-before-4/java/org/apache/dubbo/spring/boot/interceptor/DubboTagHeaderOrParameterInterceptor.java
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot/src/test/resources/META-INF/dubbo.properties b/dubbo-spring-boot-project/dubbo-spring-boot/src/test/resources/META-INF/dubbo.properties
new file mode 100644
index 000000000000..bc3b7b15384d
--- /dev/null
+++ b/dubbo-spring-boot-project/dubbo-spring-boot/src/test/resources/META-INF/dubbo.properties
@@ -0,0 +1 @@
+dubbo.application.id = test-dubbo-application
diff --git a/dubbo-test/dubbo-test-modules/src/test/java/org/apache/dubbo/dependency/FileTest.java b/dubbo-test/dubbo-test-modules/src/test/java/org/apache/dubbo/dependency/FileTest.java
index 8e2a2dfa4f95..238ee8886ed9 100644
--- a/dubbo-test/dubbo-test-modules/src/test/java/org/apache/dubbo/dependency/FileTest.java
+++ b/dubbo-test/dubbo-test-modules/src/test/java/org/apache/dubbo/dependency/FileTest.java
@@ -351,6 +351,44 @@ void checkRootSpringBoot4Profile() throws DocumentException {
SAXReader reader = new SAXReader();
Element rootElement = reader.read(new File(baseFile, "pom.xml")).getRootElement();
+ Assertions.assertTrue(
+ hasProfileManagedDependency(
+ rootElement, "spring-boot-4", "org.junit", "junit-bom", "${junit-bom.version}"),
+ "spring-boot-4 profile must import junit-bom with ${junit-bom.version}");
+
+ String[] junitJupiterArtifacts = {"junit-jupiter-api", "junit-jupiter-engine", "junit-jupiter-params"};
+ for (String artifactId : junitJupiterArtifacts) {
+ Assertions.assertTrue(
+ hasProfileManagedDependency(
+ rootElement, "spring-boot-4", "org.junit.jupiter", artifactId, "${junit-bom.version}"),
+ "spring-boot-4 profile must manage " + artifactId + " with ${junit-bom.version}");
+ }
+
+ String[] junitPlatformArtifacts = {"junit-platform-commons", "junit-platform-engine", "junit-platform-launcher"
+ };
+ for (String artifactId : junitPlatformArtifacts) {
+ Assertions.assertTrue(
+ hasProfileManagedDependency(
+ rootElement, "spring-boot-4", "org.junit.platform", artifactId, "${junit-bom.version}"),
+ "spring-boot-4 profile must manage " + artifactId + " with ${junit-bom.version}");
+ }
+
+ String[] logbackArtifacts = {"logback-classic", "logback-core"};
+ for (String artifactId : logbackArtifacts) {
+ Assertions.assertTrue(
+ hasProfileManagedDependency(
+ rootElement, "spring-boot-4", "ch.qos.logback", artifactId, "${logback-boot-4.version}"),
+ "spring-boot-4 profile must manage " + artifactId + " with ${logback-boot-4.version}");
+ }
+
+ String[] slf4jArtifacts = {"jcl-over-slf4j", "jul-to-slf4j", "log4j-over-slf4j", "slf4j-api"};
+ for (String artifactId : slf4jArtifacts) {
+ Assertions.assertTrue(
+ hasProfileManagedDependency(
+ rootElement, "spring-boot-4", "org.slf4j", artifactId, "${slf4j-boot-4.version}"),
+ "spring-boot-4 profile must manage " + artifactId + " with ${slf4j-boot-4.version}");
+ }
+
String[] springBootArtifacts = {
"spring-boot",
"spring-boot-autoconfigure",
@@ -361,14 +399,19 @@ void checkRootSpringBoot4Profile() throws DocumentException {
"spring-boot-actuator",
"spring-boot-actuator-autoconfigure",
"spring-boot-starter-actuator",
+ "spring-boot-health",
+ "spring-boot-micrometer-metrics",
"spring-boot-configuration-processor",
+ "spring-boot-restclient",
"spring-boot-starter-aop",
"spring-boot-starter-json",
"spring-boot-starter-log4j2",
"spring-boot-starter-logging",
"spring-boot-starter-tomcat",
"spring-boot-starter-validation",
- "spring-boot-starter-web"
+ "spring-boot-starter-web",
+ "spring-boot-tomcat",
+ "spring-boot-web-server"
};
for (String artifactId : springBootArtifacts) {
Assertions.assertTrue(
@@ -381,6 +424,32 @@ void checkRootSpringBoot4Profile() throws DocumentException {
"spring-boot-4 profile must manage " + artifactId + " with ${spring-boot-4.version}");
}
+ String[] springFrameworkArtifacts = {
+ "spring-aop",
+ "spring-aspects",
+ "spring-beans",
+ "spring-context",
+ "spring-context-indexer",
+ "spring-context-support",
+ "spring-core",
+ "spring-core-test",
+ "spring-expression",
+ "spring-instrument",
+ "spring-jdbc",
+ "spring-test",
+ "spring-tx",
+ "spring-web",
+ "spring-webflux",
+ "spring-webmvc",
+ "spring-websocket"
+ };
+ for (String artifactId : springFrameworkArtifacts) {
+ Assertions.assertTrue(
+ hasProfileManagedDependency(
+ rootElement, "spring-boot-4", "org.springframework", artifactId, "${spring-7.version}"),
+ "spring-boot-4 profile must manage " + artifactId + " with ${spring-7.version}");
+ }
+
String[] tomcatArtifacts = {"tomcat-embed-core", "tomcat-embed-el", "tomcat-embed-websocket"};
for (String artifactId : tomcatArtifacts) {
Assertions.assertTrue(
diff --git a/pom.xml b/pom.xml
index 54e4eaf1e872..ce1e37ea7feb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -165,9 +165,13 @@
6.2.18
+ 7.0.7
6.5.10
3.5.14
4.0.6
+ 6.0.3
+ 1.5.32
+ 2.0.17
4.33.4
@@ -1221,6 +1225,158 @@
+
+ org.junit
+ junit-bom
+ ${junit-bom.version}
+ pom
+ import
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ ${junit-bom.version}
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ ${junit-bom.version}
+
+
+ org.junit.jupiter
+ junit-jupiter-params
+ ${junit-bom.version}
+
+
+ org.junit.platform
+ junit-platform-commons
+ ${junit-bom.version}
+
+
+ org.junit.platform
+ junit-platform-engine
+ ${junit-bom.version}
+
+
+ org.junit.platform
+ junit-platform-launcher
+ ${junit-bom.version}
+
+
+ ch.qos.logback
+ logback-classic
+ ${logback-boot-4.version}
+
+
+ ch.qos.logback
+ logback-core
+ ${logback-boot-4.version}
+
+
+ org.slf4j
+ jcl-over-slf4j
+ ${slf4j-boot-4.version}
+
+
+ org.slf4j
+ jul-to-slf4j
+ ${slf4j-boot-4.version}
+
+
+ org.slf4j
+ log4j-over-slf4j
+ ${slf4j-boot-4.version}
+
+
+ org.slf4j
+ slf4j-api
+ ${slf4j-boot-4.version}
+
+
+ org.springframework
+ spring-aop
+ ${spring-7.version}
+
+
+ org.springframework
+ spring-aspects
+ ${spring-7.version}
+
+
+ org.springframework
+ spring-beans
+ ${spring-7.version}
+
+
+ org.springframework
+ spring-context
+ ${spring-7.version}
+
+
+ org.springframework
+ spring-context-indexer
+ ${spring-7.version}
+
+
+ org.springframework
+ spring-context-support
+ ${spring-7.version}
+
+
+ org.springframework
+ spring-core
+ ${spring-7.version}
+
+
+ org.springframework
+ spring-core-test
+ ${spring-7.version}
+
+
+ org.springframework
+ spring-expression
+ ${spring-7.version}
+
+
+ org.springframework
+ spring-instrument
+ ${spring-7.version}
+
+
+ org.springframework
+ spring-jdbc
+ ${spring-7.version}
+
+
+ org.springframework
+ spring-test
+ ${spring-7.version}
+
+
+ org.springframework
+ spring-tx
+ ${spring-7.version}
+
+
+ org.springframework
+ spring-web
+ ${spring-7.version}
+
+
+ org.springframework
+ spring-webflux
+ ${spring-7.version}
+
+
+ org.springframework
+ spring-webmvc
+ ${spring-7.version}
+
+
+ org.springframework
+ spring-websocket
+ ${spring-7.version}
+
org.springframework.boot
spring-boot
@@ -1261,6 +1417,16 @@
spring-boot-actuator-autoconfigure
${spring-boot-4.version}
+
+ org.springframework.boot
+ spring-boot-health
+ ${spring-boot-4.version}
+
+
+ org.springframework.boot
+ spring-boot-micrometer-metrics
+ ${spring-boot-4.version}
+
org.springframework.boot
spring-boot-starter-actuator
@@ -1271,6 +1437,11 @@
spring-boot-configuration-processor
${spring-boot-4.version}
+
+ org.springframework.boot
+ spring-boot-restclient
+ ${spring-boot-4.version}
+
org.springframework.boot
spring-boot-starter-aop
@@ -1306,6 +1477,16 @@
spring-boot-starter-web
${spring-boot-4.version}
+
+ org.springframework.boot
+ spring-boot-tomcat
+ ${spring-boot-4.version}
+
+
+ org.springframework.boot
+ spring-boot-web-server
+ ${spring-boot-4.version}
+
org.apache.tomcat.embed
tomcat-embed-core
From 236e9f4f9c779968aa1f7d0c2825fc42182f7b4d Mon Sep 17 00:00:00 2001
From: SavitarC <71271200+savitarc@users.noreply.github.com>
Date: Sun, 17 May 2026 15:55:05 +0800
Subject: [PATCH 5/8] fix(spring-boot): Isolate Boot 3 profile dependencies
---
dubbo-config/dubbo-config-spring/pom.xml | 63 ++++
dubbo-demo/dubbo-demo-spring-boot-idl/pom.xml | 28 ++
dubbo-demo/dubbo-demo-spring-boot/pom.xml | 4 +-
dubbo-plugin/dubbo-spring6-security/pom.xml | 10 +
.../dubbo-spring-boot-4-autoconfigure/pom.xml | 60 ++++
.../DubboZipkin4AutoConfigurationTests.java | 39 +++
.../pom.xml | 111 +++++++
.../dubbo-spring-boot-actuator/pom.xml | 93 +++++-
.../actuate/endpoint/DubboEndpointTest.java | 34 ++-
.../dubbo-spring-boot-autoconfigure/pom.xml | 111 +++++++
.../DubboObservationAutoConfiguration.java | 19 +-
.../src/test/java/jakarta/servlet/Filter.java | 19 ++
...DependencyCheckAutoConfigurationTests.java | 90 ++++++
.../DubboTriple3AutoConfiguration.java | 19 ++
...ubboObservationAutoConfigurationTests.java | 69 +++++
.../ObservationRegistryPostProcessor.java | 19 ++
.../dubbo-spring-boot-4-starter/pom.xml | 20 ++
.../dubbo-spring-boot/pom.xml | 73 +++++
.../org/apache/dubbo/dependency/FileTest.java | 289 +++++++++++++++++-
pom.xml | 21 ++
20 files changed, 1169 insertions(+), 22 deletions(-)
create mode 100644 dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/jakarta/servlet/Filter.java
create mode 100644 dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/DubboSpringBoot3DependencyCheckAutoConfigurationTests.java
create mode 100644 dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTriple3AutoConfiguration.java
create mode 100644 dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/observability/DubboObservationAutoConfigurationTests.java
create mode 100644 dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/org/springframework/boot/micrometer/observation/autoconfigure/ObservationRegistryPostProcessor.java
diff --git a/dubbo-config/dubbo-config-spring/pom.xml b/dubbo-config/dubbo-config-spring/pom.xml
index 363ab56fb821..568424817c78 100644
--- a/dubbo-config/dubbo-config-spring/pom.xml
+++ b/dubbo-config/dubbo-config-spring/pom.xml
@@ -29,10 +29,56 @@
false
2.7.18
+ 9.0.117
+
+
+
+ org.springframework.boot
+ spring-boot
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-starter
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-test
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-test-autoconfigure
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ ${spring-boot.version}
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-core
+ ${spring-boot.tomcat.version}
+
+
+
+
org.apache.dubbo
@@ -248,4 +294,21 @@
+
+
+
+ spring-boot-4
+
+ ${spring-boot-4.version}
+ ${tomcat.version}
+
+
+
+ jakarta.servlet
+ jakarta.servlet-api
+ test
+
+
+
+
diff --git a/dubbo-demo/dubbo-demo-spring-boot-idl/pom.xml b/dubbo-demo/dubbo-demo-spring-boot-idl/pom.xml
index 1075b795dcd0..ea281c29672b 100644
--- a/dubbo-demo/dubbo-demo-spring-boot-idl/pom.xml
+++ b/dubbo-demo/dubbo-demo-spring-boot-idl/pom.xml
@@ -122,4 +122,32 @@
+
+
+ spring-boot-3
+
+ ${spring-boot-3.version}
+
+
+
+ org.apache.dubbo
+ dubbo-spring-boot-3-autoconfigure
+ ${project.version}
+
+
+
+
+ spring-boot-4
+
+ ${spring-boot-4.version}
+
+
+
+ org.apache.dubbo
+ dubbo-spring-boot-4-autoconfigure
+ ${project.version}
+
+
+
+
diff --git a/dubbo-demo/dubbo-demo-spring-boot/pom.xml b/dubbo-demo/dubbo-demo-spring-boot/pom.xml
index 46837f538f89..f408e31aea4e 100644
--- a/dubbo-demo/dubbo-demo-spring-boot/pom.xml
+++ b/dubbo-demo/dubbo-demo-spring-boot/pom.xml
@@ -93,8 +93,8 @@
spring-boot-3
- 3.2.1
- 3.2.1
+ ${spring-boot-3.version}
+ ${spring-boot-3.version}
diff --git a/dubbo-plugin/dubbo-spring6-security/pom.xml b/dubbo-plugin/dubbo-spring6-security/pom.xml
index 97d0ade28e92..6db04e6be258 100644
--- a/dubbo-plugin/dubbo-spring6-security/pom.xml
+++ b/dubbo-plugin/dubbo-spring6-security/pom.xml
@@ -192,6 +192,16 @@
${spring-boot-3.version}
test
true
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.apache.logging.log4j
+ log4j-to-slf4j
+
+
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/pom.xml b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/pom.xml
index f3a5f3ec699e..529e85405c1d 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/pom.xml
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/pom.xml
@@ -37,6 +37,66 @@
pom
import
+
+ org.springframework.boot
+ spring-boot
+ ${spring-boot-4.version}
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+ ${spring-boot-4.version}
+
+
+ org.springframework.boot
+ spring-boot-http-client
+ ${spring-boot-4.version}
+
+
+ org.springframework.boot
+ spring-boot-http-converter
+ ${spring-boot-4.version}
+
+
+ org.springframework.boot
+ spring-boot-restclient
+ ${spring-boot-4.version}
+
+
+ org.springframework.boot
+ spring-boot-starter
+ ${spring-boot-4.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+ ${spring-boot-4.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ ${spring-boot-4.version}
+
+
+ org.springframework.boot
+ spring-boot-test
+ ${spring-boot-4.version}
+
+
+ org.springframework.boot
+ spring-boot-test-autoconfigure
+ ${spring-boot-4.version}
+
+
+ org.springframework.boot
+ spring-boot-tomcat
+ ${spring-boot-4.version}
+
+
+ org.springframework.boot
+ spring-boot-web-server
+ ${spring-boot-4.version}
+
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/DubboZipkin4AutoConfigurationTests.java b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/DubboZipkin4AutoConfigurationTests.java
index 0990559f92a5..bbb0a6f7f159 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/DubboZipkin4AutoConfigurationTests.java
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/DubboZipkin4AutoConfigurationTests.java
@@ -16,10 +16,14 @@
*/
package org.apache.dubbo.spring.boot.autoconfigure.observability.zipkin;
+import org.apache.dubbo.spring.boot.autoconfigure.SpringBoot4Condition;
+
import java.util.List;
import java.util.concurrent.TimeUnit;
import io.opentelemetry.exporter.zipkin.ZipkinSpanExporter;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.restclient.RestTemplateBuilder;
@@ -33,18 +37,31 @@
import zipkin2.reporter.Call;
import zipkin2.reporter.Encoding;
import zipkin2.reporter.Sender;
+import zipkin2.reporter.brave.ZipkinSpanHandler;
import zipkin2.reporter.urlconnection.URLConnectionSender;
import static org.assertj.core.api.Assertions.assertThat;
class DubboZipkin4AutoConfigurationTests {
+ private final boolean springBoot4 = SpringBoot4Condition.IS_SPRING_BOOT_4;
+
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(DubboZipkin4AutoConfiguration.class))
.withPropertyValues(
"dubbo.tracing.enabled=true",
"dubbo.tracing.tracing-exporter.zipkin-config.endpoint=http://localhost:9411/api/v2/spans");
+ @BeforeEach
+ void enableSpringBoot4Condition() {
+ SpringBoot4Condition.IS_SPRING_BOOT_4 = true;
+ }
+
+ @AfterEach
+ void resetSpringBoot4Condition() {
+ SpringBoot4Condition.IS_SPRING_BOOT_4 = springBoot4;
+ }
+
@Test
void shouldSupplyZipkinBeansOnSpringBoot4() {
this.contextRunner
@@ -55,6 +72,28 @@ void shouldSupplyZipkinBeansOnSpringBoot4() {
});
}
+ @Test
+ void shouldSupplyUrlConnectionSenderByDefault() {
+ this.contextRunner.run((context) -> {
+ assertThat(context).hasSingleBean(Sender.class);
+ assertThat(context.getBean(Sender.class)).isInstanceOf(URLConnectionSender.class);
+ assertThat(context).hasSingleBean(AsyncReporter.class);
+ assertThat(context).hasSingleBean(ZipkinSpanHandler.class);
+ assertThat(context).hasSingleBean(ZipkinSpanExporter.class);
+ });
+ }
+
+ @Test
+ void shouldSupplyWebClientSenderWhenUrlConnectionIsUnavailable() {
+ this.contextRunner
+ .withClassLoader(new FilteredClassLoader(URLConnectionSender.class))
+ .run((context) -> {
+ assertThat(context).hasSingleBean(Sender.class);
+ assertThat(context.getBean(Sender.class)).isInstanceOf(ZipkinWebClientSender.class);
+ assertThat(context).hasSingleBean(ZipkinSpanExporter.class);
+ });
+ }
+
@Test
void shouldSupplyRestTemplateSenderWhenUrlConnectionAndWebClientAreUnavailable() {
this.contextRunner
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/pom.xml b/dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/pom.xml
index be6cdc322312..302eb603a062 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/pom.xml
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/pom.xml
@@ -154,6 +154,117 @@
+
+ spring-boot-3
+
+ 9.0.117
+ 2.7.18
+
+
+
+
+ org.springframework.boot
+ spring-boot
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-actuator
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-actuator-autoconfigure
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-starter
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-json
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-log4j2
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-tomcat
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-test
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-test-autoconfigure
+ ${spring-boot-2.version}
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-core
+ ${spring-boot-2.tomcat.version}
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-el
+ ${spring-boot-2.tomcat.version}
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-websocket
+ ${spring-boot-2.tomcat.version}
+
+
+
+
spring-boot-4
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-actuator/pom.xml b/dubbo-spring-boot-project/dubbo-spring-boot-actuator/pom.xml
index 17358c4db740..71d232cad5fd 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-actuator/pom.xml
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-actuator/pom.xml
@@ -112,11 +112,6 @@
test
-
- org.junit.platform
- junit-platform-runner
- test
-
org.apache.dubbo
dubbo-metrics-default
@@ -131,6 +126,94 @@
true
+
+ 2.7.18
+ 9.0.117
+
+
+
+
+ org.springframework.boot
+ spring-boot
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-actuator
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-actuator-autoconfigure
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-starter
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-json
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-tomcat
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-test
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-test-autoconfigure
+ ${spring-boot.version}
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-core
+ ${spring-boot.tomcat.version}
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-el
+ ${spring-boot.tomcat.version}
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-websocket
+ ${spring-boot.tomcat.version}
+
+
+
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/test/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboEndpointTest.java b/dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/test/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboEndpointTest.java
index c623eb7802d3..621bb6007859 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/test/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboEndpointTest.java
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/test/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboEndpointTest.java
@@ -17,8 +17,11 @@
package org.apache.dubbo.spring.boot.actuate.endpoint;
import org.apache.dubbo.config.bootstrap.DubboBootstrap;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.spring.boot.actuate.endpoint.metadata.DubboMetadata;
import org.apache.dubbo.spring.boot.util.DubboUtils;
+import java.util.Collections;
import java.util.Map;
import org.junit.jupiter.api.AfterEach;
@@ -28,6 +31,8 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.TestConfiguration;
+import org.springframework.context.annotation.Bean;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.apache.dubbo.common.Version.getVersion;
@@ -43,7 +48,7 @@
*/
@ExtendWith(SpringExtension.class)
@SpringBootTest(
- classes = {DubboQosEndpoints.class},
+ classes = {DubboQosEndpoints.class, DubboEndpointTest.TestConfig.class},
properties = {"dubbo.application.name = dubbo-demo-application"})
@EnableAutoConfiguration
class DubboEndpointTest {
@@ -79,8 +84,29 @@ void testInvoke() {
assertEquals("https://github.com/apache/dubbo", urls.get("dubbo"));
assertEquals("dev@dubbo.apache.org", urls.get("mailing-list"));
- assertEquals("https://github.com/apache/dubbo-spring-boot-project", urls.get("github"));
- assertEquals("https://github.com/apache/dubbo-spring-boot-project/issues", urls.get("issues"));
- assertEquals("https://github.com/apache/dubbo-spring-boot-project.git", urls.get("git"));
+ assertEquals("https://github.com/apache/dubbo/tree/3.0/dubbo-spring-boot", urls.get("github"));
+ assertEquals("https://github.com/apache/dubbo/issues", urls.get("issues"));
+ assertEquals("https://github.com/apache/dubbo.git", urls.get("git"));
+ }
+
+ @TestConfiguration
+ static class TestConfig {
+
+ @Bean
+ ApplicationModel applicationModel() {
+ return ApplicationModel.defaultModel();
+ }
+
+ @Bean
+ DubboMetadata dubboMetadata() {
+ return new DubboMetadata();
+ }
+
+ @Bean
+ DubboActuatorProperties dubboActuatorProperties() {
+ DubboActuatorProperties properties = new DubboActuatorProperties();
+ properties.setDubbo(Collections.emptyMap());
+ return properties;
+ }
}
}
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/pom.xml b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/pom.xml
index 842748f85cc9..78b805051f40 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/pom.xml
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/pom.xml
@@ -355,6 +355,117 @@
+
+ spring-boot-3
+
+ 9.0.117
+ 2.7.18
+
+
+
+
+ org.springframework.boot
+ spring-boot
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-actuator
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-actuator-autoconfigure
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-starter
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-json
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-log4j2
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-tomcat
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-test
+ ${spring-boot-2.version}
+
+
+ org.springframework.boot
+ spring-boot-test-autoconfigure
+ ${spring-boot-2.version}
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-core
+ ${spring-boot-2.tomcat.version}
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-el
+ ${spring-boot-2.tomcat.version}
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-websocket
+ ${spring-boot-2.tomcat.version}
+
+
+
+
spring-boot-4
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/DubboObservationAutoConfiguration.java b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/DubboObservationAutoConfiguration.java
index 46f90c74d8de..b63825bd96a2 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/DubboObservationAutoConfiguration.java
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/DubboObservationAutoConfiguration.java
@@ -58,11 +58,20 @@
@ConditionalOnProperty(prefix = DUBBO_PREFIX, name = "enabled", matchIfMissing = true)
@AutoConfiguration(
after = DubboMicrometerTracingAutoConfiguration.class,
- afterName = "org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration")
+ afterName = {
+ "org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration",
+ "org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration"
+ })
@ConditionalOnDubboTracingEnable
@ConditionalOnClass(name = {"io.micrometer.observation.Observation", "io.micrometer.tracing.Tracer"})
public class DubboObservationAutoConfiguration
implements BeanFactoryAware, ApplicationListener, Ordered {
+ private static final String BOOT3_OBSERVATION_REGISTRY_POST_PROCESSOR =
+ "org.springframework.boot.actuate.autoconfigure.observation.ObservationRegistryPostProcessor";
+
+ private static final String BOOT4_OBSERVATION_REGISTRY_POST_PROCESSOR =
+ "org.springframework.boot.micrometer.observation.autoconfigure.ObservationRegistryPostProcessor";
+
private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(QosProtocolWrapper.class);
public DubboObservationAutoConfiguration(ApplicationModel applicationModel) {
@@ -82,7 +91,7 @@ ObservationRegistry observationRegistry() {
@Bean
@ConditionalOnMissingBean(
- type = "org.springframework.boot.actuate.autoconfigure.observation.ObservationRegistryPostProcessor")
+ type = {BOOT3_OBSERVATION_REGISTRY_POST_PROCESSOR, BOOT4_OBSERVATION_REGISTRY_POST_PROCESSOR})
@ConditionalOnClass(name = "io.micrometer.observation.ObservationHandler")
public ObservationRegistryPostProcessor dubboObservationRegistryPostProcessor(
ObjectProvider observationHandlerGrouping,
@@ -119,7 +128,7 @@ public int getOrder() {
@ConditionalOnClass(MeterRegistry.class)
@ConditionalOnMissingClass("io.micrometer.tracing.Tracer")
@ConditionalOnMissingBean(
- type = "org.springframework.boot.actuate.autoconfigure.observation.ObservationRegistryPostProcessor")
+ type = {BOOT3_OBSERVATION_REGISTRY_POST_PROCESSOR, BOOT4_OBSERVATION_REGISTRY_POST_PROCESSOR})
static class OnlyMetricsConfiguration {
@Bean
@@ -133,7 +142,7 @@ ObservationHandlerGrouping metricsObservationHandlerGrouping() {
@ConditionalOnClass(io.micrometer.tracing.Tracer.class)
@ConditionalOnMissingClass("io.micrometer.core.instrument.MeterRegistry")
@ConditionalOnMissingBean(
- type = "org.springframework.boot.actuate.autoconfigure.observation.ObservationRegistryPostProcessor")
+ type = {BOOT3_OBSERVATION_REGISTRY_POST_PROCESSOR, BOOT4_OBSERVATION_REGISTRY_POST_PROCESSOR})
static class OnlyTracingConfiguration {
@Bean
@@ -146,7 +155,7 @@ ObservationHandlerGrouping tracingObservationHandlerGrouping() {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({MeterRegistry.class, io.micrometer.tracing.Tracer.class})
@ConditionalOnMissingBean(
- type = "org.springframework.boot.actuate.autoconfigure.observation.ObservationRegistryPostProcessor")
+ type = {BOOT3_OBSERVATION_REGISTRY_POST_PROCESSOR, BOOT4_OBSERVATION_REGISTRY_POST_PROCESSOR})
static class MetricsWithTracingConfiguration {
@Bean
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/jakarta/servlet/Filter.java b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/jakarta/servlet/Filter.java
new file mode 100644
index 000000000000..e4cd7a97a6ea
--- /dev/null
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/jakarta/servlet/Filter.java
@@ -0,0 +1,19 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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 jakarta.servlet;
+
+public interface Filter {}
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/DubboSpringBoot3DependencyCheckAutoConfigurationTests.java b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/DubboSpringBoot3DependencyCheckAutoConfigurationTests.java
new file mode 100644
index 000000000000..111af741e67d
--- /dev/null
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/DubboSpringBoot3DependencyCheckAutoConfigurationTests.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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.apache.dubbo.spring.boot.autoconfigure;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.test.context.FilteredClassLoader;
+import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class DubboSpringBoot3DependencyCheckAutoConfigurationTests {
+
+ private static final String MISSING_BOOT3_AUTOCONFIGURE =
+ "Couldn't enable servlet support for triple at SpringBoot3: Missing dubbo-spring-boot-3-autoconfigure";
+
+ private final boolean springBoot3 = SpringBoot3Condition.IS_SPRING_BOOT_3;
+
+ private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
+ .withConfiguration(AutoConfigurations.of(DubboSpringBoot3DependencyCheckAutoConfiguration.class))
+ .withClassLoader(new FilteredClassLoader(DubboTriple3AutoConfiguration.class));
+
+ private final WebApplicationContextRunner contextRunnerWithSpringBoot3Autoconfigure =
+ new WebApplicationContextRunner()
+ .withConfiguration(AutoConfigurations.of(DubboSpringBoot3DependencyCheckAutoConfiguration.class));
+
+ @BeforeEach
+ void enableSpringBoot3Condition() {
+ SpringBoot3Condition.IS_SPRING_BOOT_3 = true;
+ }
+
+ @AfterEach
+ void resetSpringBoot3Condition() {
+ SpringBoot3Condition.IS_SPRING_BOOT_3 = springBoot3;
+ }
+
+ @Test
+ void shouldFailWhenTripleServletEnabledWithoutSpringBoot3Autoconfigure() {
+ this.contextRunner
+ .withPropertyValues("dubbo.protocol.triple.servlet.enabled=true")
+ .run(context -> {
+ assertThat(context).hasFailed();
+ assertThat(context.getStartupFailure()).hasRootCauseMessage(MISSING_BOOT3_AUTOCONFIGURE);
+ });
+ }
+
+ @Test
+ void shouldFailWhenTripleWebSocketEnabledWithoutSpringBoot3Autoconfigure() {
+ this.contextRunner
+ .withPropertyValues("dubbo.protocol.triple.websocket.enabled=true")
+ .run(context -> {
+ assertThat(context).hasFailed();
+ assertThat(context.getStartupFailure()).hasRootCauseMessage(MISSING_BOOT3_AUTOCONFIGURE);
+ });
+ }
+
+ @Test
+ void shouldNotFailWhenSpringBoot3AutoconfigureIsPresent() {
+ this.contextRunnerWithSpringBoot3Autoconfigure
+ .withPropertyValues(
+ "dubbo.protocol.triple.servlet.enabled=true", "dubbo.protocol.triple.websocket.enabled=true")
+ .run(context -> assertThat(context).hasNotFailed());
+ }
+
+ @Test
+ void shouldNotFailWhenSpringBoot3ConditionIsDisabled() {
+ SpringBoot3Condition.IS_SPRING_BOOT_3 = false;
+
+ this.contextRunner
+ .withPropertyValues(
+ "dubbo.protocol.triple.servlet.enabled=true", "dubbo.protocol.triple.websocket.enabled=true")
+ .run(context -> assertThat(context).hasNotFailed());
+ }
+}
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTriple3AutoConfiguration.java b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTriple3AutoConfiguration.java
new file mode 100644
index 000000000000..30a7e6a0bcd9
--- /dev/null
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTriple3AutoConfiguration.java
@@ -0,0 +1,19 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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.apache.dubbo.spring.boot.autoconfigure;
+
+public class DubboTriple3AutoConfiguration {}
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/observability/DubboObservationAutoConfigurationTests.java b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/observability/DubboObservationAutoConfigurationTests.java
new file mode 100644
index 000000000000..35582daf5bd2
--- /dev/null
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/observability/DubboObservationAutoConfigurationTests.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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.apache.dubbo.spring.boot.autoconfigure.observability;
+
+import org.apache.dubbo.rpc.model.ApplicationModel;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+class DubboObservationAutoConfigurationTests {
+
+ private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
+ .withConfiguration(AutoConfigurations.of(DubboObservationAutoConfiguration.class))
+ .withUserConfiguration(ApplicationModelConfiguration.class)
+ .withPropertyValues("dubbo.tracing.enabled=true");
+
+ @Test
+ void shouldBackOffObservationRegistryPostProcessorWhenSpringBoot4ProvidesOne() {
+ this.contextRunner
+ .withUserConfiguration(SpringBoot4ObservationConfiguration.class)
+ .run((context) -> {
+ assertThat(context)
+ .hasSingleBean(
+ org.springframework.boot.micrometer.observation.autoconfigure
+ .ObservationRegistryPostProcessor.class);
+ assertThat(context).doesNotHaveBean(ObservationRegistryPostProcessor.class);
+ assertThat(context).doesNotHaveBean(ObservationHandlerGrouping.class);
+ });
+ }
+
+ @Configuration(proxyBeanMethods = false)
+ private static class ApplicationModelConfiguration {
+
+ @Bean
+ ApplicationModel applicationModel() {
+ return mock(ApplicationModel.class);
+ }
+ }
+
+ @Configuration(proxyBeanMethods = false)
+ private static class SpringBoot4ObservationConfiguration {
+
+ @Bean
+ org.springframework.boot.micrometer.observation.autoconfigure.ObservationRegistryPostProcessor
+ springBoot4ObservationRegistryPostProcessor() {
+ return new org.springframework.boot.micrometer.observation.autoconfigure.ObservationRegistryPostProcessor();
+ }
+ }
+}
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/org/springframework/boot/micrometer/observation/autoconfigure/ObservationRegistryPostProcessor.java b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/org/springframework/boot/micrometer/observation/autoconfigure/ObservationRegistryPostProcessor.java
new file mode 100644
index 000000000000..da5a223c1ff8
--- /dev/null
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/org/springframework/boot/micrometer/observation/autoconfigure/ObservationRegistryPostProcessor.java
@@ -0,0 +1,19 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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.micrometer.observation.autoconfigure;
+
+public class ObservationRegistryPostProcessor {}
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-starters/dubbo-spring-boot-4-starter/pom.xml b/dubbo-spring-boot-project/dubbo-spring-boot-starters/dubbo-spring-boot-4-starter/pom.xml
index 529c60a9fb48..89ea76d15e21 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-starters/dubbo-spring-boot-4-starter/pom.xml
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-starters/dubbo-spring-boot-4-starter/pom.xml
@@ -37,6 +37,26 @@
pom
import
+
+ org.springframework.boot
+ spring-boot
+ ${spring-boot-4.version}
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+ ${spring-boot-4.version}
+
+
+ org.springframework.boot
+ spring-boot-starter
+ ${spring-boot-4.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+ ${spring-boot-4.version}
+
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot/pom.xml b/dubbo-spring-boot-project/dubbo-spring-boot/pom.xml
index 051358f08fe7..b78395874851 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot/pom.xml
+++ b/dubbo-spring-boot-project/dubbo-spring-boot/pom.xml
@@ -106,6 +106,79 @@
true
+
+ 2.7.18
+ 9.0.117
+
+
+
+
+ org.springframework.boot
+ spring-boot
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-starter
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-json
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-tomcat
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-test
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-test-autoconfigure
+ ${spring-boot.version}
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-core
+ ${spring-boot.tomcat.version}
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-el
+ ${spring-boot.tomcat.version}
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-websocket
+ ${spring-boot.tomcat.version}
+
+
+
diff --git a/dubbo-test/dubbo-test-modules/src/test/java/org/apache/dubbo/dependency/FileTest.java b/dubbo-test/dubbo-test-modules/src/test/java/org/apache/dubbo/dependency/FileTest.java
index 238ee8886ed9..07cc9b83d21a 100644
--- a/dubbo-test/dubbo-test-modules/src/test/java/org/apache/dubbo/dependency/FileTest.java
+++ b/dubbo-test/dubbo-test-modules/src/test/java/org/apache/dubbo/dependency/FileTest.java
@@ -241,6 +241,7 @@ void checkSpringBootDemosSupportSpringBoot4SmokeCompile() throws DocumentExcepti
List demoPomPaths = new LinkedList<>();
demoPomPaths.add("dubbo-demo" + File.separator + "dubbo-demo-spring-boot" + File.separator
+ "dubbo-demo-spring-boot-servlet" + File.separator + "pom.xml");
+ demoPomPaths.add("dubbo-demo" + File.separator + "dubbo-demo-spring-boot-idl" + File.separator + "pom.xml");
demoPomPaths.add("dubbo-demo" + File.separator + "dubbo-demo-mcp-server" + File.separator + "pom.xml");
for (String demoPomPath : demoPomPaths) {
@@ -261,6 +262,131 @@ void checkSpringBootDemosSupportSpringBoot4SmokeCompile() throws DocumentExcepti
}
}
+ @Test
+ void checkSpringBootIdlDemoUsesRequestedSpringBootProfiles() throws DocumentException {
+ File baseFile = getBaseFile();
+ SAXReader reader = new SAXReader();
+ String demoPomPath = "dubbo-demo" + File.separator + "dubbo-demo-spring-boot-idl" + File.separator + "pom.xml";
+ Element rootElement = reader.read(new File(baseFile, demoPomPath)).getRootElement();
+
+ Assertions.assertTrue(
+ hasProfileProperty(rootElement, "spring-boot-3", "spring-boot.version", "${spring-boot-3.version}"),
+ demoPomPath + " must use ${spring-boot-3.version} under spring-boot-3");
+ Assertions.assertTrue(
+ hasProfileProperty(rootElement, "spring-boot-4", "spring-boot.version", "${spring-boot-4.version}"),
+ demoPomPath + " must use ${spring-boot-4.version} under spring-boot-4");
+ }
+
+ @Test
+ void checkSpringBootDemoUsesRequestedSpringBootProfiles() throws DocumentException {
+ File baseFile = getBaseFile();
+ SAXReader reader = new SAXReader();
+ String demoPomPath = "dubbo-demo" + File.separator + "dubbo-demo-spring-boot" + File.separator + "pom.xml";
+ Element rootElement = reader.read(new File(baseFile, demoPomPath)).getRootElement();
+
+ Assertions.assertTrue(
+ hasProfileProperty(rootElement, "spring-boot-3", "spring-boot.version", "${spring-boot-3.version}"),
+ demoPomPath + " must use ${spring-boot-3.version} under spring-boot-3");
+ Assertions.assertTrue(
+ hasProfileProperty(
+ rootElement, "spring-boot-3", "spring-boot-maven-plugin.version", "${spring-boot-3.version}"),
+ demoPomPath
+ + " must use ${spring-boot-3.version} for the Spring Boot Maven plugin under spring-boot-3");
+ Assertions.assertTrue(
+ hasProfileProperty(rootElement, "spring-boot-4", "spring-boot.version", "${spring-boot-4.version}"),
+ demoPomPath + " must use ${spring-boot-4.version} under spring-boot-4");
+ Assertions.assertTrue(
+ hasProfileProperty(
+ rootElement, "spring-boot-4", "spring-boot-maven-plugin.version", "${spring-boot-4.version}"),
+ demoPomPath
+ + " must use ${spring-boot-4.version} for the Spring Boot Maven plugin under spring-boot-4");
+ }
+
+ @Test
+ void checkSpringBootMcpServerUsesRequestedSpringBootPluginProfiles() throws DocumentException {
+ File baseFile = getBaseFile();
+ SAXReader reader = new SAXReader();
+ String demoPomPath = "dubbo-demo" + File.separator + "dubbo-demo-mcp-server" + File.separator + "pom.xml";
+ Element rootElement = reader.read(new File(baseFile, demoPomPath)).getRootElement();
+
+ Assertions.assertTrue(
+ hasProfileProperty(
+ rootElement, "spring-boot-3", "spring-boot-maven-plugin.version", "${spring-boot-3.version}"),
+ demoPomPath
+ + " must use ${spring-boot-3.version} for the Spring Boot Maven plugin under spring-boot-3");
+ Assertions.assertTrue(
+ hasProfileProperty(
+ rootElement, "spring-boot-4", "spring-boot-maven-plugin.version", "${spring-boot-4.version}"),
+ demoPomPath
+ + " must use ${spring-boot-4.version} for the Spring Boot Maven plugin under spring-boot-4");
+ }
+
+ @Test
+ void checkSpringBootModulesKeepVersionedSourceProfiles() throws DocumentException {
+ File baseFile = getBaseFile();
+ SAXReader reader = new SAXReader();
+ String[][] modules = {
+ {
+ "dubbo-spring-boot-project" + File.separator + "dubbo-spring-boot" + File.separator + "pom.xml",
+ "src/spring-boot-before-4/java",
+ "src/spring-boot-4/java"
+ },
+ {
+ "dubbo-spring-boot-project" + File.separator + "dubbo-spring-boot-actuator" + File.separator
+ + "pom.xml",
+ "src/spring-boot-before-4/java",
+ "src/spring-boot-4/java"
+ },
+ {
+ "dubbo-plugin" + File.separator + "dubbo-rest-spring" + File.separator + "pom.xml",
+ "src/spring-boot-before-4/java",
+ "src/spring-boot-4/java"
+ }
+ };
+
+ for (String[] module : modules) {
+ Element rootElement = reader.read(new File(baseFile, module[0])).getRootElement();
+ Assertions.assertTrue(
+ hasProfileSource(rootElement, "spring-boot-before-4", module[1]),
+ module[0] + " must keep its Spring Boot before 4 source set");
+ Assertions.assertTrue(
+ hasProfileSource(rootElement, "spring-boot-4", module[2]),
+ module[0] + " must keep its Spring Boot 4 source set");
+ }
+ }
+
+ @Test
+ void checkDubboConfigSpringUsesSpringBoot4Profile() throws DocumentException {
+ File baseFile = getBaseFile();
+ SAXReader reader = new SAXReader();
+ String pomPath = "dubbo-config" + File.separator + "dubbo-config-spring" + File.separator + "pom.xml";
+ Element rootElement = reader.read(new File(baseFile, pomPath)).getRootElement();
+
+ Assertions.assertTrue(
+ hasProfileProperty(rootElement, "spring-boot-4", "spring-boot.version", "${spring-boot-4.version}"),
+ pomPath + " must use ${spring-boot-4.version} under spring-boot-4");
+ Assertions.assertTrue(
+ hasProfileProperty(rootElement, "spring-boot-4", "spring-boot.tomcat.version", "${tomcat.version}"),
+ pomPath + " must use ${tomcat.version} under spring-boot-4");
+ }
+
+ @Test
+ void checkDubboBomIncludesSpringBoot4Artifacts() throws DocumentException {
+ File baseFile = getBaseFile();
+ SAXReader reader = new SAXReader();
+ String bomPath = "dubbo-distribution" + File.separator + "dubbo-bom" + File.separator + "pom.xml";
+ Element rootElement = reader.read(new File(baseFile, bomPath)).getRootElement();
+ String[] artifactIds = {
+ "dubbo-spring-boot-3-autoconfigure", "dubbo-spring-boot-4-autoconfigure", "dubbo-spring-boot-4-starter"
+ };
+
+ for (String artifactId : artifactIds) {
+ Assertions.assertTrue(
+ hasManagedDependency(rootElement, "org.apache.dubbo", artifactId, "${project.version}"),
+ "dubbo-bom must include " + artifactId);
+ }
+ }
+
@Test
void checkSpringBootDependencyCheckAutoConfigurationsRegistered() throws IOException {
File baseFile = getBaseFile();
@@ -389,6 +515,15 @@ void checkRootSpringBoot4Profile() throws DocumentException {
"spring-boot-4 profile must manage " + artifactId + " with ${slf4j-boot-4.version}");
}
+ Assertions.assertTrue(
+ hasProfileManagedDependency(
+ rootElement,
+ "spring-boot-4",
+ "com.fasterxml.jackson.core",
+ "jackson-annotations",
+ "${jackson-annotations-boot-4.version}"),
+ "spring-boot-4 profile must manage jackson-annotations with ${jackson-annotations-boot-4.version}");
+
String[] springBootArtifacts = {
"spring-boot",
"spring-boot-autoconfigure",
@@ -402,6 +537,9 @@ void checkRootSpringBoot4Profile() throws DocumentException {
"spring-boot-health",
"spring-boot-micrometer-metrics",
"spring-boot-configuration-processor",
+ "spring-boot-http-client",
+ "spring-boot-http-converter",
+ "spring-boot-jackson",
"spring-boot-restclient",
"spring-boot-starter-aop",
"spring-boot-starter-json",
@@ -459,6 +597,51 @@ void checkRootSpringBoot4Profile() throws DocumentException {
}
}
+ @Test
+ void checkRootSpringBoot3Profile() throws DocumentException {
+ File baseFile = getBaseFile();
+ SAXReader reader = new SAXReader();
+ Element rootElement = reader.read(new File(baseFile, "pom.xml")).getRootElement();
+
+ String[] springBootArtifacts = {
+ "spring-boot",
+ "spring-boot-autoconfigure",
+ "spring-boot-starter",
+ "spring-boot-test",
+ "spring-boot-test-autoconfigure",
+ "spring-boot-starter-test",
+ "spring-boot-actuator",
+ "spring-boot-actuator-autoconfigure",
+ "spring-boot-starter-actuator",
+ "spring-boot-configuration-processor",
+ "spring-boot-starter-aop",
+ "spring-boot-starter-json",
+ "spring-boot-starter-log4j2",
+ "spring-boot-starter-logging",
+ "spring-boot-starter-tomcat",
+ "spring-boot-starter-validation",
+ "spring-boot-starter-web"
+ };
+ for (String artifactId : springBootArtifacts) {
+ Assertions.assertTrue(
+ hasProfileManagedDependency(
+ rootElement,
+ "spring-boot-3",
+ "org.springframework.boot",
+ artifactId,
+ "${spring-boot-3.version}"),
+ "spring-boot-3 profile must manage " + artifactId + " with ${spring-boot-3.version}");
+ }
+
+ String[] tomcatArtifacts = {"tomcat-embed-core", "tomcat-embed-el", "tomcat-embed-websocket"};
+ for (String artifactId : tomcatArtifacts) {
+ Assertions.assertTrue(
+ hasProfileManagedDependency(
+ rootElement, "spring-boot-3", "org.apache.tomcat.embed", artifactId, "${tomcat.version}"),
+ "spring-boot-3 profile must manage " + artifactId + " with ${tomcat.version}");
+ }
+ }
+
@Test
void checkSpringBootCompatibleKeepsSpringBoot1DependencyManagement() throws DocumentException {
File baseFile = getBaseFile();
@@ -550,6 +733,62 @@ void checkSpringBootAutoconfigureKeepsBefore4DependencyManagement() throws Docum
Element rootElement =
reader.read(new File(baseFile, springBootAutoconfigurePath)).getRootElement();
+ String[] profileIds = {"spring-boot-3", "spring-boot-4"};
+ String[] springBootBefore4Artifacts = {
+ "spring-boot",
+ "spring-boot-autoconfigure",
+ "spring-boot-starter",
+ "spring-boot-test",
+ "spring-boot-test-autoconfigure",
+ "spring-boot-starter-test",
+ "spring-boot-actuator",
+ "spring-boot-actuator-autoconfigure",
+ "spring-boot-starter-actuator",
+ "spring-boot-configuration-processor",
+ "spring-boot-starter-aop",
+ "spring-boot-starter-json",
+ "spring-boot-starter-log4j2",
+ "spring-boot-starter-logging",
+ "spring-boot-starter-tomcat",
+ "spring-boot-starter-validation",
+ "spring-boot-starter-web"
+ };
+ String[] tomcatBefore4Artifacts = {"tomcat-embed-core", "tomcat-embed-el", "tomcat-embed-websocket"};
+ for (String profileId : profileIds) {
+ for (String artifactId : springBootBefore4Artifacts) {
+ Assertions.assertTrue(
+ hasProfileManagedDependency(
+ rootElement,
+ profileId,
+ "org.springframework.boot",
+ artifactId,
+ "${spring-boot-2.version}"),
+ "dubbo-spring-boot-autoconfigure must manage " + artifactId
+ + " with ${spring-boot-2.version} under " + profileId);
+ }
+ for (String artifactId : tomcatBefore4Artifacts) {
+ Assertions.assertTrue(
+ hasProfileManagedDependency(
+ rootElement,
+ profileId,
+ "org.apache.tomcat.embed",
+ artifactId,
+ "${spring-boot-2.tomcat.version}"),
+ "dubbo-spring-boot-autoconfigure must manage " + artifactId
+ + " with ${spring-boot-2.tomcat.version} under " + profileId);
+ }
+ }
+ }
+
+ @Test
+ void checkSpringBootActuatorAutoconfigureKeepsBefore4DependencyManagement() throws DocumentException {
+ File baseFile = getBaseFile();
+ SAXReader reader = new SAXReader();
+ String springBootActuatorAutoconfigurePath = "dubbo-spring-boot-project" + File.separator
+ + "dubbo-spring-boot-actuator-autoconfigure" + File.separator + "pom.xml";
+ Element rootElement = reader.read(new File(baseFile, springBootActuatorAutoconfigurePath))
+ .getRootElement();
+
String[] springBootBefore4Artifacts = {
"spring-boot",
"spring-boot-autoconfigure",
@@ -573,12 +812,12 @@ void checkSpringBootAutoconfigureKeepsBefore4DependencyManagement() throws Docum
Assertions.assertTrue(
hasProfileManagedDependency(
rootElement,
- "spring-boot-4",
+ "spring-boot-3",
"org.springframework.boot",
artifactId,
"${spring-boot-2.version}"),
- "dubbo-spring-boot-autoconfigure must manage " + artifactId
- + " with ${spring-boot-2.version} under spring-boot-4");
+ "dubbo-spring-boot-actuator-autoconfigure must manage " + artifactId
+ + " with ${spring-boot-2.version} under spring-boot-3");
}
String[] tomcatBefore4Artifacts = {"tomcat-embed-core", "tomcat-embed-el", "tomcat-embed-websocket"};
@@ -586,12 +825,12 @@ void checkSpringBootAutoconfigureKeepsBefore4DependencyManagement() throws Docum
Assertions.assertTrue(
hasProfileManagedDependency(
rootElement,
- "spring-boot-4",
+ "spring-boot-3",
"org.apache.tomcat.embed",
artifactId,
"${spring-boot-2.tomcat.version}"),
- "dubbo-spring-boot-autoconfigure must manage " + artifactId
- + " with ${spring-boot-2.tomcat.version} under spring-boot-4");
+ "dubbo-spring-boot-actuator-autoconfigure must manage " + artifactId
+ + " with ${spring-boot-2.tomcat.version} under spring-boot-3");
}
}
@@ -1219,6 +1458,44 @@ private boolean hasProfileDependency(Element rootElement, String profileId, Stri
.anyMatch(dependency -> Objects.equals(artifactId, dependency.elementText("artifactId")));
}
+ private boolean hasProfileProperty(Element rootElement, String profileId, String propertyName, String value) {
+ Element profiles = rootElement.element("profiles");
+ if (profiles == null) {
+ return false;
+ }
+ return profiles.elements("profile").stream()
+ .filter(profile -> Objects.equals(profileId, profile.elementText("id")))
+ .map(profile -> profile.element("properties"))
+ .filter(Objects::nonNull)
+ .anyMatch(properties -> Objects.equals(value, properties.elementText(propertyName)));
+ }
+
+ private boolean hasProfileSource(Element rootElement, String profileId, String source) {
+ Element profiles = rootElement.element("profiles");
+ if (profiles == null) {
+ return false;
+ }
+ return profiles.elements("profile").stream()
+ .filter(profile -> Objects.equals(profileId, profile.elementText("id")))
+ .map(profile -> profile.element("build"))
+ .filter(Objects::nonNull)
+ .map(build -> build.element("plugins"))
+ .filter(Objects::nonNull)
+ .map(plugins -> plugins.elements("plugin"))
+ .flatMap(Collection::stream)
+ .map(plugin -> plugin.element("executions"))
+ .filter(Objects::nonNull)
+ .map(executions -> executions.elements("execution"))
+ .flatMap(Collection::stream)
+ .map(execution -> execution.element("configuration"))
+ .filter(Objects::nonNull)
+ .map(configuration -> configuration.element("sources"))
+ .filter(Objects::nonNull)
+ .map(sources -> sources.elements("source"))
+ .flatMap(Collection::stream)
+ .anyMatch(sourceElement -> Objects.equals(source, sourceElement.getTextTrim()));
+ }
+
private boolean hasDependency(Element rootElement, String artifactId) {
Element dependencies = rootElement.element("dependencies");
if (dependencies == null) {
diff --git a/pom.xml b/pom.xml
index ce1e37ea7feb..7b886d17b3e8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -169,6 +169,7 @@
6.5.10
3.5.14
4.0.6
+ 2.21
6.0.3
1.5.32
2.0.17
@@ -1232,6 +1233,11 @@
pom
import
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+ ${jackson-annotations-boot-4.version}
+
org.junit.jupiter
junit-jupiter-api
@@ -1437,6 +1443,21 @@
spring-boot-configuration-processor
${spring-boot-4.version}
+
+ org.springframework.boot
+ spring-boot-http-client
+ ${spring-boot-4.version}
+
+
+ org.springframework.boot
+ spring-boot-http-converter
+ ${spring-boot-4.version}
+
+
+ org.springframework.boot
+ spring-boot-jackson
+ ${spring-boot-4.version}
+
org.springframework.boot
spring-boot-restclient
From e3463d4f3bfd674d8d1cb7ab6a469d7686b87b21 Mon Sep 17 00:00:00 2001
From: SavitarC <71271200+savitarc@users.noreply.github.com>
Date: Mon, 18 May 2026 13:56:06 +0800
Subject: [PATCH 6/8] fix: correct Boot 4 Zipkin and REST profile config
---
dubbo-plugin/dubbo-rest-spring/pom.xml | 72 ++++++++-----------
.../zipkin/DubboZipkin4AutoConfiguration.java | 2 +-
.../DubboZipkin4AutoConfigurationTests.java | 10 +++
3 files changed, 39 insertions(+), 45 deletions(-)
diff --git a/dubbo-plugin/dubbo-rest-spring/pom.xml b/dubbo-plugin/dubbo-rest-spring/pom.xml
index ed3ca7f7293b..c99cc65a6158 100644
--- a/dubbo-plugin/dubbo-rest-spring/pom.xml
+++ b/dubbo-plugin/dubbo-rest-spring/pom.xml
@@ -26,6 +26,10 @@
dubbo-rest-spring
+
+ src/spring-boot-before-4/java
+
+
org.apache.dubbo
@@ -113,6 +117,24 @@
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+
+
+ add-spring-boot-sources
+
+ add-source
+
+ generate-sources
+
+
+ ${spring-boot.source.dir}
+
+
+
+
+
@@ -122,31 +144,15 @@
true
-
-
-
- org.codehaus.mojo
- build-helper-maven-plugin
-
-
- add-spring-boot-before-4-sources
-
- add-source
-
- generate-sources
-
-
- src/spring-boot-before-4/java
-
-
-
-
-
-
-
+
+ src/spring-boot-before-4/java
+
spring-boot-4
+
+ src/spring-boot-4/java
+
jakarta.servlet
@@ -154,28 +160,6 @@
provided
-
-
-
- org.codehaus.mojo
- build-helper-maven-plugin
-
-
- add-spring-boot-4-sources
-
- add-source
-
- generate-sources
-
-
- src/spring-boot-4/java
-
-
-
-
-
-
-
rpc-rest-test
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/DubboZipkin4AutoConfiguration.java b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/DubboZipkin4AutoConfiguration.java
index c38ff34461a3..511fd5280d91 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/DubboZipkin4AutoConfiguration.java
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/DubboZipkin4AutoConfiguration.java
@@ -48,7 +48,7 @@
@AutoConfiguration(
afterName = {
"org.springframework.boot.restclient.autoconfigure.RestTemplateAutoConfiguration",
- "org.springframework.boot.actuate.autoconfigure.tracing.zipkin"
+ "org.springframework.boot.zipkin.autoconfigure.ZipkinAutoConfiguration"
})
@ConditionalOnClass(Sender.class)
@Import({
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/DubboZipkin4AutoConfigurationTests.java b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/DubboZipkin4AutoConfigurationTests.java
index bbb0a6f7f159..e35cadcdf086 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/DubboZipkin4AutoConfigurationTests.java
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/DubboZipkin4AutoConfigurationTests.java
@@ -25,6 +25,7 @@
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.restclient.RestTemplateBuilder;
import org.springframework.boot.test.context.FilteredClassLoader;
@@ -72,6 +73,15 @@ void shouldSupplyZipkinBeansOnSpringBoot4() {
});
}
+ @Test
+ void shouldOrderAfterSpringBootZipkinAutoConfiguration() {
+ AutoConfiguration autoConfiguration =
+ DubboZipkin4AutoConfiguration.class.getAnnotation(AutoConfiguration.class);
+
+ assertThat(autoConfiguration.afterName())
+ .contains("org.springframework.boot.zipkin.autoconfigure.ZipkinAutoConfiguration");
+ }
+
@Test
void shouldSupplyUrlConnectionSenderByDefault() {
this.contextRunner.run((context) -> {
From 2c9e1ea3608e221d8795ad4efc95e35eb253260a Mon Sep 17 00:00:00 2001
From: SavitarC <71271200+savitarc@users.noreply.github.com>
Date: Mon, 18 May 2026 15:34:28 +0800
Subject: [PATCH 7/8] fix(spring-boot): guard Boot 4 auto configuration checks
---
.../DubboTriple4AutoConfiguration.java | 32 ++++----
.../DubboTriple4AutoConfigurationTests.java | 14 ++++
.../autoconfigure/SpringBoot4Condition.java | 3 +-
.../SpringBootBefore4Condition.java | 4 +-
.../autoconfigure/SpringBootVersionUtils.java | 78 +++++++++++++++++++
.../SpringBootConditionTests.java | 19 +++++
6 files changed, 131 insertions(+), 19 deletions(-)
create mode 100644 dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBootVersionUtils.java
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTriple4AutoConfiguration.java b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTriple4AutoConfiguration.java
index 54dbe31b3f7d..aad826ebc4e9 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTriple4AutoConfiguration.java
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTriple4AutoConfiguration.java
@@ -63,22 +63,26 @@ public FilterRegistrationBean tripleProtocolFilter(
return registrationBean;
}
- @Bean
- @ConditionalOnClass(Http2Protocol.class)
+ @Configuration(proxyBeanMethods = false)
+ @ConditionalOnClass({ConfigurableTomcatWebServerFactory.class, Http2Protocol.class})
@ConditionalOnProperty(prefix = SERVLET_PREFIX, name = "max-concurrent-streams")
- public WebServerFactoryCustomizer tripleTomcatHttp2Customizer(
- @Value("${" + SERVLET_PREFIX + ".max-concurrent-streams}") int maxConcurrentStreams) {
- return factory -> factory.addConnectorCustomizers(connector -> {
- ProtocolHandler handler = connector.getProtocolHandler();
- for (UpgradeProtocol upgradeProtocol : handler.findUpgradeProtocols()) {
- if (upgradeProtocol instanceof Http2Protocol) {
- Http2Protocol protocol = (Http2Protocol) upgradeProtocol;
- int value = maxConcurrentStreams <= 0 ? Integer.MAX_VALUE : maxConcurrentStreams;
- protocol.setMaxConcurrentStreams(value);
- protocol.setMaxConcurrentStreamExecution(value);
+ public static class TomcatHttp2Configuration {
+
+ @Bean
+ public WebServerFactoryCustomizer tripleTomcatHttp2Customizer(
+ @Value("${" + SERVLET_PREFIX + ".max-concurrent-streams}") int maxConcurrentStreams) {
+ return factory -> factory.addConnectorCustomizers(connector -> {
+ ProtocolHandler handler = connector.getProtocolHandler();
+ for (UpgradeProtocol upgradeProtocol : handler.findUpgradeProtocols()) {
+ if (upgradeProtocol instanceof Http2Protocol) {
+ Http2Protocol protocol = (Http2Protocol) upgradeProtocol;
+ int value = maxConcurrentStreams <= 0 ? Integer.MAX_VALUE : maxConcurrentStreams;
+ protocol.setMaxConcurrentStreams(value);
+ protocol.setMaxConcurrentStreamExecution(value);
+ }
}
- }
- });
+ });
+ }
}
}
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTriple4AutoConfigurationTests.java b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTriple4AutoConfigurationTests.java
index bc1b385d1795..2b7c1bb6b224 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTriple4AutoConfigurationTests.java
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-4-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTriple4AutoConfigurationTests.java
@@ -23,6 +23,7 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
@@ -88,4 +89,17 @@ void shouldRegisterTomcatHttp2CustomizerWhenMaxConcurrentStreamsConfigured() {
"dubbo.protocol.triple.servlet.max-concurrent-streams=10")
.run(context -> assertThat(context).hasSingleBean(WebServerFactoryCustomizer.class));
}
+
+ @Test
+ void shouldIgnoreTomcatHttp2CustomizerWhenTomcatIsNotPresent() {
+ this.contextRunner
+ .withClassLoader(new FilteredClassLoader("org.springframework.boot.tomcat", "org.apache.coyote"))
+ .withPropertyValues(
+ "dubbo.protocol.triple.servlet.enabled=true",
+ "dubbo.protocol.triple.servlet.max-concurrent-streams=10")
+ .run(context -> {
+ assertThat(context).hasNotFailed();
+ assertThat(context).doesNotHaveBean(WebServerFactoryCustomizer.class);
+ });
+ }
}
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBoot4Condition.java b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBoot4Condition.java
index dd75baba12ac..db803dab1380 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBoot4Condition.java
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBoot4Condition.java
@@ -16,14 +16,13 @@
*/
package org.apache.dubbo.spring.boot.autoconfigure;
-import org.springframework.boot.SpringBootVersion;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class SpringBoot4Condition implements Condition {
- public static boolean IS_SPRING_BOOT_4 = SpringBootVersion.getVersion().charAt(0) == '4';
+ public static boolean IS_SPRING_BOOT_4 = SpringBootVersionUtils.isSpringBoot4();
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBootBefore4Condition.java b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBootBefore4Condition.java
index 144363813275..771c4d5d513b 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBootBefore4Condition.java
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBootBefore4Condition.java
@@ -16,15 +16,13 @@
*/
package org.apache.dubbo.spring.boot.autoconfigure;
-import org.springframework.boot.SpringBootVersion;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class SpringBootBefore4Condition implements Condition {
- public static boolean IS_SPRING_BOOT_BEFORE_4 =
- SpringBootVersion.getVersion().charAt(0) < '4';
+ public static boolean IS_SPRING_BOOT_BEFORE_4 = SpringBootVersionUtils.isBeforeSpringBoot4();
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBootVersionUtils.java b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBootVersionUtils.java
new file mode 100644
index 000000000000..b240e9dd5617
--- /dev/null
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBootVersionUtils.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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.apache.dubbo.spring.boot.autoconfigure;
+
+import org.springframework.boot.SpringBootVersion;
+import org.springframework.util.ClassUtils;
+
+final class SpringBootVersionUtils {
+
+ private static final int UNKNOWN_MAJOR_VERSION = -1;
+
+ private static final int SPRING_BOOT_4_MAJOR_VERSION = 4;
+
+ private static final String SPRING_BOOT_4_MARKER_CLASS =
+ "org.springframework.boot.autoconfigure.condition.ConditionalOnMissingFilterBean";
+
+ private SpringBootVersionUtils() {}
+
+ static boolean isSpringBoot4() {
+ return isSpringBoot4(SpringBootVersion.getVersion());
+ }
+
+ static boolean isSpringBoot4(String version) {
+ int majorVersion = getMajorVersion(version);
+ if (majorVersion != UNKNOWN_MAJOR_VERSION) {
+ return majorVersion == SPRING_BOOT_4_MAJOR_VERSION;
+ }
+ return isSpringBoot4Present();
+ }
+
+ static boolean isBeforeSpringBoot4() {
+ return isBeforeSpringBoot4(SpringBootVersion.getVersion());
+ }
+
+ static boolean isBeforeSpringBoot4(String version) {
+ int majorVersion = getMajorVersion(version);
+ if (majorVersion != UNKNOWN_MAJOR_VERSION) {
+ return majorVersion < SPRING_BOOT_4_MAJOR_VERSION;
+ }
+ return !isSpringBoot4Present();
+ }
+
+ static int getMajorVersion(String version) {
+ if (version == null) {
+ return UNKNOWN_MAJOR_VERSION;
+ }
+ String trimmedVersion = version.trim();
+ if (trimmedVersion.isEmpty()) {
+ return UNKNOWN_MAJOR_VERSION;
+ }
+ int endIndex = 0;
+ while (endIndex < trimmedVersion.length() && Character.isDigit(trimmedVersion.charAt(endIndex))) {
+ endIndex++;
+ }
+ if (endIndex == 0) {
+ return UNKNOWN_MAJOR_VERSION;
+ }
+ return Integer.parseInt(trimmedVersion.substring(0, endIndex));
+ }
+
+ private static boolean isSpringBoot4Present() {
+ return ClassUtils.isPresent(SPRING_BOOT_4_MARKER_CLASS, SpringBootVersionUtils.class.getClassLoader());
+ }
+}
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBootConditionTests.java b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBootConditionTests.java
index d51bd5cca6ff..3f4196c4e431 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBootConditionTests.java
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBootConditionTests.java
@@ -48,4 +48,23 @@ void shouldMatchOnlySpringBoot4ConditionOnSpringBoot4() {
Assertions.assertFalse(new SpringBootBefore4Condition().matches(null, null));
Assertions.assertTrue(new SpringBoot4Condition().matches(null, null));
}
+
+ @Test
+ void shouldParseSpringBootVersionsNullSafely() {
+ Assertions.assertTrue(SpringBootVersionUtils.getMajorVersion(null) < 0);
+ Assertions.assertTrue(SpringBootVersionUtils.getMajorVersion("") < 0);
+ Assertions.assertEquals(3, SpringBootVersionUtils.getMajorVersion("3.5.14"));
+ Assertions.assertEquals(4, SpringBootVersionUtils.getMajorVersion("4.0.6"));
+
+ Assertions.assertFalse(SpringBootVersionUtils.isSpringBoot4("3.5.14"));
+ Assertions.assertTrue(SpringBootVersionUtils.isBeforeSpringBoot4("3.5.14"));
+ Assertions.assertTrue(SpringBootVersionUtils.isSpringBoot4("4.0.6"));
+ Assertions.assertFalse(SpringBootVersionUtils.isBeforeSpringBoot4("4.0.6"));
+ }
+
+ @Test
+ void shouldEvaluateConditionsWhenSpringBootVersionIsUnavailable() {
+ Assertions.assertDoesNotThrow(() -> SpringBootVersionUtils.isSpringBoot4(null));
+ Assertions.assertDoesNotThrow(() -> SpringBootVersionUtils.isBeforeSpringBoot4(null));
+ }
}
From b0335826c1e4decc0d9b56b338965af9d7254494 Mon Sep 17 00:00:00 2001
From: SavitarC <71271200+SavitarC@users.noreply.github.com>
Date: Mon, 18 May 2026 16:41:31 +0800
Subject: [PATCH 8/8] fix: order zipkin auto-config after spring boot class
---
.../observability/zipkin/ZipkinAutoConfiguration.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/ZipkinAutoConfiguration.java b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/ZipkinAutoConfiguration.java
index 68d2a7b2c807..8742ffcf6ac8 100644
--- a/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/ZipkinAutoConfiguration.java
+++ b/dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/ZipkinAutoConfiguration.java
@@ -52,7 +52,7 @@
@AutoConfiguration(
afterName = {
"org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration",
- "org.springframework.boot.actuate.autoconfigure.tracing.zipkin"
+ "org.springframework.boot.actuate.autoconfigure.tracing.zipkin.ZipkinAutoConfiguration"
})
@ConditionalOnClass(Sender.class)
@Import({