RSocket responders can be implemented as methods with @MessageMapping and @ConnectMapping annotations. Methods annotated with @MessageMapping handle individual requests, while methods annotated with @ConnectMapping handle connection-level events (setting and pushing metadata). Annotated responders are supported symmetrically for both server-side and client-side responses.

Server-side responders

To use annotated server-side responders, add RSocketMessageHandler into the Spring configuration to detect beans marked with the @Controller annotation with methods annotated with @MessageMapping and @ConnectMapping:

Java

@Configuration
static class ServerConfig {
@Bean
public RSocketMessageHandler rsocketMessageHandler() {
RSocketMessageHandler handler = new RSocketMessageHandler();
handler.routeMatcher(new PathPatternRouteMatcher());
return handler;
}
}
Kotlin

@Configuration
class ServerConfig {
@Bean
fun rsocketMessageHandler() = RSocketMessageHandler().apply {
routeMatcher = PathPatternRouteMatcher()
}
}

Then start the RSocket server via the RSocket API for Java and connect the RSocketMessageHandler to the responder like this:

Java

ApplicationContext context = ... ;
RSocketMessageHandler handler = context.getBean(RSocketMessageHandler.class);
CloseableChannel server =
RSocketServer.create(handler.responder())
.bind(TcpServerTransport.create("localhost", 7000))
.block();
Kotlin

import org.springframework.beans.factory.getBean
val context: ApplicationContext = ...
val handler = context.getBean<RSocketMessageHandler>()
val server = RSocketServer.create(handler.responder())
.bind(TcpServerTransport.create("localhost", 7000))
.awaitSingle()

RSocketMessageHandler by default supports composite metadata and routing. You can configure its MetadataExtractor if you need to switch to a different MIME type or register additional MIME metadata types.

You will need to configure the Encoder and Decoder instances, that are required to support metadata and data formats. To implement codecs, you will most likely need the spring-web module.

By default, SimpleRouteMatcher is used to negotiate routes through AntPathMatcher . We recommend connecting PathPatternRouteMatcher from spring-web for efficient route matching. RSocket routes can be hierarchical, but are not URL paths. Both route matchers are configured to use "." as the default delimiter, and there is no URL decoding as is the case with HTTP URLs.

RSocketMessageHandler can be configured via RSocketStrategies, which can be useful if you need to share configuration between client and server in the same process:

Java

@Configuration
static class ServerConfig {
@Bean
public RSocketMessageHandler rsocketMessageHandler() {
RSocketMessageHandler handler = new RSocketMessageHandler();
handler.setRSocketStrategies(rsocketStrategies());
return handler;
}
@Bean
public RSocketStrategies rsocketStrategies() {
return RSocketStrategies.builder()
    .encoders(encoders -> encoders.add(new Jackson2CborEncoder()))
    .decoders(decoders -> decoders.add(new Jackson2CborDecoder()))
    .routeMatcher(new PathPatternRouteMatcher())
    .build();
}
}
Kotlin

@Configuration
class ServerConfig {
@Bean
fun rsocketMessageHandler() = RSocketMessageHandler().apply {
rSocketStrategies = rsocketStrategies()
}
@Bean
fun rsocketStrategies() = RSocketStrategies.builder()
    .encoders { it.add(Jackson2CborEncoder()) }
    .decoders { it.add(Jackson2CborDecoder()) }
    .routeMatcher(PathPatternRouteMatcher())
    .build()
}

Client-side responders

Annotated client-side responders must be configured in RSocketRequester.Builder .

@MessageMapping

Once the responder configuration for the server or client has been configured, methods with the @MessageMapping annotation can be used as follows:

Java

@Controller
public class RadarsController {
@MessageMapping("locate.radars.within")
public Flux<AirportLocation> radars(MapRequest request) {
// ...
}
}
Kotlin

@Controller
class RadarsController {
@MessageMapping("locate.radars.within")
fun radars(request: MapRequest): Flow<AirportLocation> {
// ...
}
}

The above method, marked with the @MessageMapping annotation, responds to an interaction of type Request-Stream with the route "locate.radars.within". It supports a flexible method signature with the ability to use the following method arguments:

Method argument Description

@Payload

Request payload. This can be a concrete value of asynchronous types such as Mono or Flux.

Note: The use of an annotation is optional. A method argument that is neither a prime type nor one of the other supported arguments is taken to be the expected payload.

RSocketRequester

Requester for transmitting requests to the receiving side.

@DestinationVariable

The value extracted from the route based on variables in the mapping template, for example @MessageMapping("find.radar.{id}").

@Header

Metadata value registered to retrieve.

@Headers Map<String, Object>

All metadata values registered for retrieval.

The return value is expected to be one or more objects that will be serialized as useful response data. These can be asynchronous types such as Mono or Flux, a concrete value, or void or an asynchronous type without a value such as Mono<Void>.

The type of RSocket interaction that a method annotated with @MessageMapping supports is determined by the cardinality of the input set (i.e. the argument annotated with @Payload) and output, where cardinality means the following:

Power of the set Description

1

Either an explicit value or an asynchronous single-value type such as Mono<T>.

Many

A multivalued asynchronous type, such as Flux<T>.

0

In the case of input, this means that the method does not have an argument annotated with @Payload.

In the case of output, this is void or a valueless asynchronous type such as Mono<Void>.

The table below shows all input and output set power combinations and the corresponding interaction type(s):

Input set cardinality Output set cardinality Interaction types

0, 1

0

Fire-and-Forget, Request-Response

0, 1

1

Request-Response

0, 1

Set

Request-Stream

Set

0, 1, set

Request-Channel

@ConnectMapping

The @ConnectMapping annotation handles the SETUP frame at the beginning of an RSocket connection establishment and all subsequent notifications about pushing metadata through frame METADATA_PUSH, i.e. metadataPush(Payload) in io.rsocket.RSocket.

Methods with the @ConnectMapping annotation support the same arguments as and methods with the @MessageMapping annotation, but rely on metadata and data from the SETUP and METADATA_PUSH frames. The @ConnectMapping annotation can be provided with a template to limit the processing procedure to specific connections that have a route in the metadata, and if no templates are declared, then all connections will match.

Methods annotated @ConnectMapping cannot return data, so they must be declared using void or Mono<Void> as the return value. If the processing procedure returns an error for a new connection, then the connection is rejected. The processing procedure should not be delayed so that requests to RSocketRequester can be made for a specific connection.