Sunday, December 20, 2020

Spring WebFlux Example Using Functional Programming - Spring Web Reactive

In this post we’ll see a Spring web reactive example using Spring WebFlux functional programming model. The application built here is a RESTful web service with Spring Webflux and also includes a WebClient consumer of that service. Application uses Spring Boot and run on the default Netty server.


Maven dependency for Spring WebFlux

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.netjs</groupId>
  <artifactId>reactive</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>reactive</name>
  <url>http://maven.apache.org</url>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.5.RELEASE</version>
    <relativePath/>
  </parent>
    
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <jdk.version>1.10</jdk.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

The dependency you need to add is spring-boot-starter-webflux which takes care of getting all the jar dependencies like reactor-core, reactive-streams, spring-webflux along with the Netty server. Spring Boot 2 uses Netty by default with WebFlux because Netty is more widely used in the async, non-blocking space and also provides both client and server that can share resources.

Spring WebFlux example – functional endpoint

For Spring WebFlux functional programming model you need to provide the following components-

  • Handler function- Incoming HTTP requests are handled by a HandlerFunction, which is essentially a function that takes a ServerRequest and returns a Mono<ServerResponse>. It is recommended to group related handler functions into a handler or controller class.
  • RouterFunction- For mapping incoming requests to appropriate handler functions RouterFunction is used. RouterFunction takes a ServerRequest, and returns a Mono<HandlerFunction>. If a request matches a particular route, a handler function is returned, or otherwise an empty Mono is returned. RouterFunction has a similar purpose as the @RequestMapping annotation in the annotation-based programming model.

In the example here we also have a model bean User and a reactive repository that is exposed in the handler class for the data operations. To keep the application simple we’ll use a Map to store data rather than going to an actual reactive repository like reactive Mongo.

Reactive WebFlux application - Model bean

The model bean used is User.java class-

public class User {
 private long id;
 private String firstName;
 private String lastName;
 private String email;
 
 public User() {
   
 }
 public User(long id, String firstName, String lastName, String email) {
  this.id = id;
  this.firstName = firstName;
  this.lastName = lastName;
  this.email = email;
 }
 public long getId() {
  return id;
 }
 public void setId(long id) {
  this.id = id;
 }
 public String getFirstName() {
  return firstName;
 }
 public void setFirstName(String firstName) {
  this.firstName = firstName;
 }
 public String getLastName() {
  return lastName;
 }
 public void setLastName(String lastName) {
  this.lastName = lastName;
 }
 public String getEmail() {
  return email;
 }
 public void setEmail(String email) {
  this.email = email;
 }
}

Spring Reactive WebFlux application – Repository

public interface UserRepository {
  Mono<User> getUserById(int id);
    
  Flux<User> getAllUsers();
    
  Mono<Void> saveUser(Mono<User> user);
}

As you can see there are three methods-

  1. getUserById fetches a single User by Id. This method returns a Mono.
  2. getAllUsers fetches all the Users. This method returns a Flux.
  3. saveUser method saves the passed User object. This method returns Mono<Void> which is an empty Mono that emits a completion signal when the user has been read from the request and stored.

UserRepositoryImpl.java

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.netjs.model.User;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

public class UserRepositoryImpl implements UserRepository {
    
  Map<Integer, User> userMap = new ConcurrentHashMap<Integer, User>(); 
  public UserRepositoryImpl(){
    // adding entries to Map
    userMap.put(1, new User(1, "Robert", "Ludlum", "rl@rl.com"));
    userMap.put(2, new User(2, "John", "Grisham", "jg@jg.com"));
    userMap.put(3, new User(3, "James", "Patterson", "jp@jp.com"));
  }

  @Override
  public Mono<User> getUserById(int id) {
    return Mono.justOrEmpty(userMap.get(id));
  }

  @Override
  public Flux<User> getAllUsers() {
    // get as stream
    return Flux.fromStream(userMap.values().stream());
  }

  @Override
  public Mono<Void> saveUser(Mono<User> user) {    
    Mono<User> userMono = user.doOnNext(value->{
      userMap.put((userMap.keySet().size() +1), value);            
    } );
    return userMono.then();
  }    
}

Spring Reactive WebFlux application – Handler functions

Handler functions are grouped together in a UserHandler class.

import org.netjs.model.User;
import org.netjs.repository.UserRepository;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

public class UserHandler {
  private final UserRepository userRepository;

  public UserHandler(UserRepository userRepository) {
    this.userRepository = userRepository;
  }
  public Mono<ServerResponse> listUser(ServerRequest request) {
    Flux<User> user = userRepository.getAllUsers();
    return ServerResponse.ok().contentType(APPLICATION_JSON).body(user, User.class);
  }
    
  public Mono<ServerResponse> getUser(ServerRequest request) {
    int userId = Integer.valueOf(request.pathVariable("id"));
    Mono<ServerResponse> notFound = ServerResponse.notFound().build();
    Mono<User> userMono = userRepository.getUserById(userId);
    return userMono.flatMap(user -> ServerResponse.ok()
          .contentType(APPLICATION_JSON)
          .body(BodyInserters.fromObject(user)))
          .switchIfEmpty(notFound);
  }
    
  public Mono<ServerResponse> createUser(ServerRequest request) {
    System.out.println("in create user");
    Mono<User> user = request.bodyToMono(User.class);
    return ServerResponse.ok().build(userRepository.saveUser(user));
  }
}

listUser is a handler function that returns all User objects found in the repository as JSON.

createUser is a handler function that stores a new User contained in the request body. Since UserRepository.saveUser(User) returns Mono<Void> so build(Publisher<Void>) method is used to send a response when that completion signal is received, i.e. when the User has been saved.

getUser is a handler function that returns a single user, identified via the path variable id. That user is retrieved from the repository, and a JSON response is created if it is found. If it is not found, we use switchIfEmpty(Mono<T>) to return a 404 Not Found response.

Spring Reactive WebFlux application – Router functions

We also need to define router function that routes to the respective handler functions. We use method-references to refer to the handler functions.

@Configuration
public class UserRouter {
  @Bean
  public RouterFunction<ServerResponse> route() {
    UserRepository repository = new UserRepositoryImpl();
    UserHandler userHandler = new UserHandler(repository);
    return RouterFunctions
        .route(GET("/user/{id}").and(accept(APPLICATION_JSON)), userHandler::getUser)
        .andRoute(GET("/user").and(accept(APPLICATION_JSON)), userHandler::listUser)
        .andRoute(POST("/user/create").and(contentType(APPLICATION_JSON)), userHandler::createUser);
    
  }
}

Creating a WebClient

For reactive applications, Spring framework offers the WebClient class, which is non-blocking. We’ll use a WebClient implementation to consume our RESTful service.

import java.util.List;
import org.netjs.model.User;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

public class UserWebClient {
  private WebClient client = WebClient.create("http://localhost:8080");
  // For getting all users
  private Mono<ClientResponse> result = client.get()
        .uri("/user")
        .accept(MediaType.APPLICATION_JSON_UTF8)
        .exchange();

  // Getting user by ID
  private Mono<User> singleUser = client.get()
        .uri("/user/1")
        .accept(MediaType.APPLICATION_JSON_UTF8)
        .exchange()
        .flatMap(res -> res.bodyToMono(User.class));
    
  public List<User> getResult() {
    Flux<User> userList = result.flatMapMany(res -> res.bodyToFlux(User.class));
    return userList.collectList().block();
  }
}

Running Spring WebFlux application as a standalone application

We’ll configure the Spring WebFlux application as a standalone application using the @SpringBootApplication convenience annotation.

@SpringBootApplication
public class Application {

 public static void main(String[] args) {
  SpringApplication.run(Application.class, args);
  UserWebClient uwc = new UserWebClient();
  System.out.println(uwc.getResult());
 }
}

The main() method uses Spring Boot’s SpringApplication.run() method to launch an application.

You can run the above class as stand alone Java application and it will display the result it gets through WebClient.

Running the Spring WebFlux REST application

Now we have the WebClient to consume the Restful service and the Spring boot configuration to launch the application.

You can run the application from the command line with Gradle or Maven. You can also build a single executable JAR file that contains all the necessary dependencies, classes, and resources, and run that.

With Maven, you can run the application using mvn spring-boot:run command from terminal. Or you can build the JAR file with mvn clean package. Then you can run the JAR file- java -jar target/reactive-0.0.1-SNAPSHOT.jar

When using mvn spring-boot:run command from your project directory to launch the application, on the successful start of the application you should see on the console that the netty server is started and listening on default port 8080.

2018-11-02 10:37:54.151  INFO 8664 --- [           main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port(s): 8080
2018-11-02 10:37:54.151  INFO 8664 --- [           main] org.netjs.reactive.Application           : Started Application in 4.033 seconds (JVM running for 15.002)

From the browser you can access the following URL to fetch all the Users -

http://localhost:8080/user
Spring WebFlux example

To get user by ID

http://localhost:8080/user/2
Spring web reactive example

To save a user

To save a user you can use the following curl command.

curl localhost:8080/user/create -H "Content-Type: application/json" -X POST -d "{\"id\":4, \"firstName\":\"Dean\",\"lastName\":\"Koontz\", \"email\":\"dk@dk.com\"}"  
Reference: https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-fn

That's all for this topic Spring WebFlux Example Using Functional Programming - Spring Web Reactive. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Spring Tutorial Page


Related Topics

  1. Spring Web Reactive - Spring WebFlux Example Using Annotation-Based Programming
  2. Spring Thread Pooling Support Using TaskExecutor
  3. Sending Email Using Spring Framework Example
  4. Spring MVC Example With @PathVaribale - Creating Dynamic URL
  5. @Resource Annotation in Spring Autowiring

You may also like-

  1. Using c-namespace in Spring
  2. Lazy Initialization in Spring Using lazy-init And @Lazy Annotation
  3. Data Access in Spring Framework
  4. Transaction Management in Spring
  5. How ArrayList Works Internally in Java
  6. Java Multithreading Interview Questions And Answers
  7. Converting String to Enum Type in Java
  8. Parquet File Format in Hadoop

No comments:

Post a Comment