Concurrency is a vital aspect of web applications and it allows them to handle multiple tasks simultaneously without blocking. Spring WebFlux was introduced in Spring 5. It provides a reactive programming model that enables the non-blocking, asynchronous processing. This approach leverages the reactor library and it allows the developers to build efficient and scalable web applications.
Reactive Programming
Reactive programming is a programming paradigm that handles asynchronous data streams and the propagation of changes. In Spring WebFlux, this is achieved using Project Reactor which provides two main types:
- Mono: It represents the single asynchronous value or the empty value.
- Flux: It represents the stream of 0 to N asynchronous values.
Non-blocking and Asynchronous Processing
Spring WebFlux allows us to handle the requests asynchronously and process multiple requests concurrently without blocking the treads. This results in better resource utilization and improved the application performance.
Implementation of Concurrency in Spring Webflux
Step 1: Create the Spring Reactive Project
Create a new Spring Reactive Project using Spring Initializr and add the below mentioned dependencies.
Dependencies:
- Spring web Reactive
- Spring Data JPA
- MySQL Driver
- Spring DevTools
- Lombok
After the project creation done, the structure of the project in the IDE will be look like the below image:

Step 2: Configure the Application Properties
Open the application.properties file and add the below properties for configuration of the MySQL database.
spring.application.name=concurrency-spring
spring.liquibase.enabled=false
spring.datasource.url=jdbc:mysql://localhost:3306/webfluxdb
spring.datasource.username=root
spring.datasource.password=
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto=updateStep 3: Create the User class
Create a simple User model class in the model package of the project.
Go to src > main > Java > com.concurrencyspring > model > User and put the below code.
package com.concurrencyspring.model;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* The User class is a JPA entity representing the 'user' table in the database.
* It uses Lombok annotations to generate boilerplate code like getters, setters, constructors, etc.
*/
@Data // Lombok annotation to generate getters, setters, toString, equals, and hashcode methods
@AllArgsConstructor // Lombok annotation to generate a constructor with all arguments
@NoArgsConstructor // Lombok annotation to generate a no-argument constructor
@Entity // Specifies that the class is an entity and is mapped to a database table
public class User {
@Id // Specifies the primary key of the entity
@GeneratedValue(strategy = GenerationType.AUTO) // Provides specification for the primary key generation strategy
private Long id; // The primary key field
private String name; // The 'name' column in the 'user' table
private String email; // The 'email' column in the 'user' table
}
Step 4: Create the UserRepository class
Create the UserRepository interface in the repository package.
Go to src > main > Java > com.concurrencyspring > repository > UserRepository and put the below code.
package com.concurrencyspring.repository;
import com.concurrencyspring.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
/**
* UserRepository is an interface that extends JpaRepository to provide CRUD operations for User entities.
* This interface will be automatically implemented by Spring Data JPA.
*/
@Repository // Indicates that the interface is a Spring Data repository
public interface UserRepository extends JpaRepository<User, Long> {
// JpaRepository provides methods for CRUD operations and more
// User is the type of the entity to handle
// Long is the type of the entity's identifier
}
Step 5: Create the UserService class
Create the UserService class in the service package. This class should use reactive types.
Go to src > main > Java > com.concurrencyspring > service > UserService and put the below code.
package com.concurrencyspring.service;
import com.concurrencyspring.model.User;
import com.concurrencyspring.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
import java.util.Optional;
/**
* Service class for managing User entities.
* Provides methods for retrieving and saving users.
*/
@Service // Indicates that this class is a Spring service component
public class UserService {
@Autowired // Marks a constructor, field, setter method, or config method to be autowired by Spring's dependency injection facilities
private UserRepository userRepository;
/**
* Retrieves a user by its ID.
*
* @param id the ID of the user to retrieve
* @return an Optional containing the user if found, or empty if not found
*/
public Optional<User> getUserById(Long id) {
return userRepository.findById(id);
}
/**
* Saves a user entity.
*
* @param user the user entity to save
* @return the saved user entity
*/
public User saveUser(User user) {
return userRepository.save(user);
}
}
Step 6: Create the UserController class
Create the UserController class in the controller package. This class should also use reactive types.
Go to src > main > Java > com.concurrencyspring > controller > UserController and put the below code.
package com.concurrencyspring.controller;
import com.concurrencyspring.model.User;
import com.concurrencyspring.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Mono;
/**
* UserController is a REST controller for managing User entities.
* Provides endpoints for retrieving and creating users.
*/
@RestController // Indicates that this class is a REST controller
@RequestMapping("/users") // Maps requests to /users
public class UserController {
@Autowired // Marks a constructor, field, setter method, or config method to be autowired by Spring's dependency injection facilities
private UserService userService;
/**
* Retrieves a user by its ID.
*
* @param id the ID of the user to retrieve
* @return a Mono containing the user if found, or empty if not found
*/
@GetMapping("/{id}") // Maps GET requests to /users/{id}
public Mono<User> getUserById(@PathVariable Long id) {
return userService.getUserById(id);
}
/**
* Creates a new user.
*
* @param user the user entity to create
* @return a Mono containing the created user entity
*/
@PostMapping // Maps POST requests to /users
public Mono<User> createUser(@RequestBody User user) {
return userService.saveUser(user);
}
}
Step 7: Main class
No changes are required in the main class of the application.
package com.concurrencyspring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ConcurrencySpringApplication {
public static void main(String[] args) {
SpringApplication.run(ConcurrencySpringApplication.class, args);
}
}
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://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.3.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.auxentios</groupId>
<artifactId>concurrency-spring</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>concurrency-spring</name>
<description>concurrency-spring</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</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>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
Step 8: Run the application
Run the applicationand it will start on port 8080 Refer the below image for better understanding.

Step 9: Testing the Endpoints
Now, we will test the endpoints by using Postman tool.
1. Create the User Endpoint:
POST http://localhost:8080/usersOutput:

2. Get the User By Id
GET http://localhost:8080/users/1Output:

This example project demonstrates how to implement the concurrency in Spring WebFlux application. The key aspects include the setting up the reactive web service and defining the reactive data model and creating the non-blocking controllers and services.