Spring Web Flux
Spring WebFlux is a reactive web framework for building reactive web applications. It is part of the Spring Framework. It is built on top of Project Reactor.
Project Reactor is a reactive programming library for building asynchronous, non-blocking applications.
Spring WebFlux is a powerful tool for building reactive, scalable web applications in high concurrency applications.
Mono and Flux
Spring WebFlux majorly uses two publishers: Mono and Flux
Mono: Returns 0 or 1 element.
The Mono API allows producing only one value.
Flux: Returns 0…N elements.
The Flux can be endless, it can produce multiple values.
Mono vs Flux
Mono and Flux are both implementations of the Publisher interface. When we’re doing some computations or making a request to a database or an external service, and expecting a maximum of one result, then Mono should be used.
When multiple results are expected, then we should use Flux.
Spring WebFlux Reactive CRUD REST API Example
Create a spring starter project
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.jts</groupId>
<artifactId>boot-webflux</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>boot-webflux</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Project Structure
Actor.java
package com.jts.model;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Document(value = "actors")
public class Actor {
@Id
private String id;
private String firstName;
private String lastName;
private String email;
}
ActorRepository.java
package com.jts.repo;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import com.jts.model.Actor;
public interface ActorRepository extends ReactiveCrudRepository<Actor, String> {
}
ActorService.java
package com.jts.service;
import com.jts.model.Actor;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
public interface ActorService {
Mono<Actor> saveActor(Actor Actor);
Mono<Actor> getActor(String actorId);
Flux<Actor> getAllActors();
Mono<Actor> updateActor(Actor Actor, String actorId);
Mono<Void> deleteActor(String actorId);
}
ActorServiceImpl.java
package com.jts.service;
import org.springframework.stereotype.Service;
import com.jts.model.Actor;
import com.jts.repo.ActorRepository;
import lombok.AllArgsConstructor;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@Service
@AllArgsConstructor
public class ActorServiceImpl implements ActorService {
private ActorRepository actorRepository;
@Override
public Mono<Actor> saveActor(Actor Actor) {
Mono<Actor> savedActor = actorRepository.save(Actor);
return savedActor;
}
@Override
public Mono<Actor> getActor(String ActorId) {
return actorRepository.findById(ActorId);
}
@Override
public Flux<Actor> getAllActors() {
return actorRepository.findAll();
}
@Override
public Mono<Actor> updateActor(Actor Actor, String ActorId) {
Mono<Actor> ActorMono = actorRepository.findById(ActorId);
return ActorMono.flatMap((existingActor) -> {
existingActor.setFirstName(Actor.getFirstName());
existingActor.setLastName(Actor.getLastName());
existingActor.setEmail(Actor.getEmail());
return actorRepository.save(existingActor);
});
}
@Override
public Mono<Void> deleteActor(String ActorId) {
return actorRepository.deleteById(ActorId);
}
}
ActorController.java
package com.jts.controller;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import com.jts.model.Actor;
import com.jts.service.ActorService;
import lombok.AllArgsConstructor;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@RestController
@RequestMapping("/api/actors")
@AllArgsConstructor
public class ActorController {
private ActorService actorService;
@PostMapping
@ResponseStatus(value = HttpStatus.CREATED)
public Mono<Actor> saveActor(@RequestBody Actor Actor) {
return actorService.saveActor(Actor);
}
@GetMapping("{id}")
public Mono<Actor> getActor(@PathVariable("id") String ActorId) {
return actorService.getActor(ActorId);
}
@GetMapping
public Flux<Actor> getAllActors() {
return actorService.getAllActors();
}
@PutMapping("{id}")
public Mono<Actor> updateActor(@RequestBody Actor Actor,
@PathVariable("id") String ActorId) {
return actorService.updateActor(Actor, ActorId);
}
@DeleteMapping("{id}")
@ResponseStatus(value = HttpStatus.NO_CONTENT)
public Mono<Void> deleteActor(@PathVariable("id") String ActorId) {
return actorService.deleteActor(ActorId);
}
}
application.properties
spring.data.mongodb.uri=mongodb+srv://abc:[email protected]/?retryWrites=true&w=majority
spring.data.mongodb.database=my_db
Run and test the application
Create actor data
Get all actors
Get Actor by id