From 485180df4f6c059c09dae443f0799c0d1238aced Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Mon, 6 Oct 2025 11:24:51 +0200 Subject: [PATCH] Polish "Register controller advices to RSocket messaging" See gh-45360 --- .../reference/pages/messaging/rsocket.adoc | 2 ++ .../RSocketMessagingAutoConfiguration.java | 35 +++++++++++++++++-- ...SocketMessagingAutoConfigurationTests.java | 5 ++- 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/messaging/rsocket.adoc b/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/messaging/rsocket.adoc index 01d739d6212..41d944b3ef1 100644 --- a/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/messaging/rsocket.adoc +++ b/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/messaging/rsocket.adoc @@ -69,6 +69,8 @@ Spring Boot will auto-configure the Spring Messaging infrastructure for RSocket. This means that Spring Boot will create a javadoc:org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler[] bean that will handle RSocket requests to your application. +TIP: You can use {url-spring-framework-docs}/web/webmvc/mvc-controller/ann-advice.html[`@ControllerAdvice`] to handle exceptions. + [[messaging.rsocket.requester]] diff --git a/module/spring-boot-rsocket/src/main/java/org/springframework/boot/rsocket/autoconfigure/RSocketMessagingAutoConfiguration.java b/module/spring-boot-rsocket/src/main/java/org/springframework/boot/rsocket/autoconfigure/RSocketMessagingAutoConfiguration.java index 400d7404d42..6613c2142db 100644 --- a/module/spring-boot-rsocket/src/main/java/org/springframework/boot/rsocket/autoconfigure/RSocketMessagingAutoConfiguration.java +++ b/module/spring-boot-rsocket/src/main/java/org/springframework/boot/rsocket/autoconfigure/RSocketMessagingAutoConfiguration.java @@ -26,6 +26,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.messaging.handler.MessagingAdviceBean; import org.springframework.messaging.rsocket.RSocketRequester; import org.springframework.messaging.rsocket.RSocketStrategies; @@ -38,6 +39,7 @@ import org.springframework.web.method.ControllerAdviceBean; * * @author Brian Clozel * @author Dmitry Sulman + * @author Stephane Nicoll * @since 4.0.0 */ @AutoConfiguration(after = RSocketStrategiesAutoConfiguration.class) @@ -51,12 +53,39 @@ public final class RSocketMessagingAutoConfiguration { RSocketMessageHandler messageHandler = new RSocketMessageHandler(); messageHandler.setRSocketStrategies(rSocketStrategies); customizers.orderedStream().forEach((customizer) -> customizer.customize(messageHandler)); - ControllerAdviceBean.findAnnotatedBeans(context) - .forEach((controllerAdviceBean) -> messageHandler - .registerMessagingAdvice(new ControllerAdviceBeanWrapper(controllerAdviceBean))); + return messageHandler; } + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass(ControllerAdviceBean.class) + static class MessagingAdviceConfiguration { + + @Bean + MessagingAdviceRSocketMessageHandlerCustomizer messagingAdviceRSocketMessageHandlerCustomizer( + ApplicationContext applicationContext) { + return new MessagingAdviceRSocketMessageHandlerCustomizer(applicationContext); + } + + } + + static final class MessagingAdviceRSocketMessageHandlerCustomizer implements RSocketMessageHandlerCustomizer { + + private final ApplicationContext applicationContext; + + MessagingAdviceRSocketMessageHandlerCustomizer(ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + + @Override + public void customize(RSocketMessageHandler messageHandler) { + ControllerAdviceBean.findAnnotatedBeans(this.applicationContext) + .forEach((controllerAdviceBean) -> messageHandler + .registerMessagingAdvice(new ControllerAdviceBeanWrapper(controllerAdviceBean))); + } + + } + private static final class ControllerAdviceBeanWrapper implements MessagingAdviceBean { private final ControllerAdviceBean adviceBean; diff --git a/module/spring-boot-rsocket/src/test/java/org/springframework/boot/rsocket/autoconfigure/RSocketMessagingAutoConfigurationTests.java b/module/spring-boot-rsocket/src/test/java/org/springframework/boot/rsocket/autoconfigure/RSocketMessagingAutoConfigurationTests.java index 18610f7244e..5576701deb4 100644 --- a/module/spring-boot-rsocket/src/test/java/org/springframework/boot/rsocket/autoconfigure/RSocketMessagingAutoConfigurationTests.java +++ b/module/spring-boot-rsocket/src/test/java/org/springframework/boot/rsocket/autoconfigure/RSocketMessagingAutoConfigurationTests.java @@ -89,15 +89,14 @@ class RSocketMessagingAutoConfigurationTests { void shouldRegisterControllerAdvice() { this.contextRunner.withBean(TestControllerAdvice.class).withBean(TestController.class).run((context) -> { RSocketMessageHandler handler = context.getBean(RSocketMessageHandler.class); - TestControllerAdvice controllerAdvice = context.getBean(TestControllerAdvice.class); - MessageHeaderAccessor headers = new MessageHeaderAccessor(); RouteMatcher.Route route = handler.getRouteMatcher().parseRoute("exception"); headers.setHeader(DestinationPatternsMessageCondition.LOOKUP_DESTINATION_HEADER, route); headers.setHeader(RSocketFrameTypeMessageCondition.FRAME_TYPE_HEADER, FrameType.REQUEST_FNF); Message message = MessageBuilder.createMessage(Mono.empty(), headers.getMessageHeaders()); + StepVerifier.create(handler.handleMessage(message)).expectComplete().verify(); - assertThat(controllerAdvice.isExceptionHandled()).isTrue(); + assertThat(context.getBean(TestControllerAdvice.class).isExceptionHandled()).isTrue(); }); }