Spring 5 WebFlux provides functional programming model for reactive web applications. Using this model is an alternative to using Spring MVC style annotations (last Example). This style routes a given HTTP requests via a RouterFunction (alternative to using annotations like @RequestMapping) and handles the request via HandlerFunction (alternative to @Controller's handler methods).
HandlerFunction
package org.springframework.web.reactive.function.server;
.....
@FunctionalInterface
public interface HandlerFunction<T extends ServerResponse> {
Mono<T> handle(ServerRequest request);
}
Where T is the type of the response of the function.
ServerRequest
This object represents a server-side HTTP request. We can access headers and body of the request via this object.
ServerResponse
This object represents a server-side HTTP response.
RouterFunction
package org.springframework.web.reactive.function.server;
....
@FunctionalInterface
public interface RouterFunction<T extends ServerResponse> {
Mono<HandlerFunction<T>> route(ServerRequest request);
...
}
Creating a RouterFunction
To create a RouterFunction instance, we will typically use RouterFunctions utility class. For example following method will return the RouterFunction:
public static <T extends ServerResponse> RouterFunction<T> route(RequestPredicate predicate,
HandlerFunction<T> handlerFunction)
RequestPredicate
RequestPredicate is another Java 8 FunctionalInterface which tests a given ServerRequest to be acceptable for this routing:
package org.springframework.web.reactive.function.server;
...
@FunctionalInterface
public interface RequestPredicate {
boolean test(ServerRequest request);
...
}
Creating a RequestPredicate
RequestPredicates is another utility class which will help us to create a RequestPredicate quickly, for example following method will create the predicate to match a path pattern:
public static RequestPredicate path(String pattern)
Example
In following example, we are going to use Spring Boot 2.0.0.RELEASE. We need to register our RouterFunction as a bean so that Spring WebFlux runtime can use it to handle HTTP requests.
Maven Dependencies
pom.xml<project .....> <modelVersion>4.0.0</modelVersion> <groupId>com.logicbig.example</groupId> <artifactId>spring-reactive-functional-helloworld</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.0.RELEASE</version> </parent> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>1.5.8.RELEASE</version> </plugin> </plugins> </build>
</project>
Defining our HandlerFunction and RouterFunction
package com.logicbig.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.reactive.config.EnableWebFlux;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
@SpringBootApplication
@EnableWebFlux
public class ExampleApplication {
@Bean
RouterFunction<ServerResponse> helloRouterFunction() {
RouterFunction<ServerResponse> routerFunction =
RouterFunctions.route(RequestPredicates.path("/"),
serverRequest ->
ServerResponse.ok().body(Mono.just("Hello World!"), String.class));
return routerFunction;
}
public static void main(String[] args) throws Exception {
SpringApplication.run(ExampleApplication.class);
}
}
To make it more readable:
@Bean
RouterFunction<ServerResponse> helloRouterFunction() {
HandlerFunction<ServerResponse> handlerFunction = serverRequest ->
ServerResponse.ok().body(Mono.just("Hello World!"), String.class);
RouterFunction<ServerResponse> routerFunction =
RouterFunctions.route(RequestPredicates.path("/"), handlerFunction);
return routerFunction;
}
Grouping handler functions in classes
It is recommended to group related handler functions into handler or controller classes and use Java 8 method references in the RouterFunctions.
Rewriting our above example:
package com.logicbig.example;
import org.springframework.web.reactive.function.server.HandlerFunction;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
public class HelloHandler {
public Mono<ServerResponse> handleRequest(ServerRequest serverRequest) {
return ServerResponse.ok().body(Mono.just("Hello World!"), String.class);
}
}
@SpringBootApplication
@EnableWebFlux
public class ExampleApplication {
@Bean
HelloHandler helloHandler() {
return new HelloHandler();
}
@Bean
RouterFunction<ServerResponse> helloRouterFunction(HelloHandler helloHandler) {
return RouterFunctions.route(RequestPredicates.path("/"),
helloHandler::handleRequest);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(ExampleApplication.class);
}
}
Running:
To try examples, run spring-boot maven plugin (configured in pom.xml of example project below):
mvn spring-boot:run
We can also run the main class from our IDE.
Output
Example ProjectDependencies and Technologies Used: - Spring Boot 2.0.0.RELEASE
Corresponding Spring Version 5.0.4.RELEASE - spring-boot-starter-webflux : Starter for building WebFlux applications using Spring Framework's
Reactive Web support.
- JDK 1.8
- Maven 3.3.9
|