Spring WebFlux (Mono/Flux) with AOP Triggering REST Call at Interception and Working with Mono/Flux
Last Updated :
23 Jul, 2025
Spring WebFlux is a component of the Spring Framework that supports reactive programming. It enables developers to build asynchronous, non-blocking applications. WebFlux uses Project Reactor as its reactive library and introduces two main types: Mono and Flux. Aspect-Oriented Programming (AOP) in Spring allows for the separation of cross-cutting concerns such as logging or transaction management. By combining WebFlux with AOP, we can trigger REST calls within the AOP interceptor and handle the results using reactive types.
This article will guide you on how to set up a Spring WebFlux project with AOP to trigger REST calls during method interception and handle these calls using Mono and Flux.
Spring WebFlux Overview
Spring WebFlux is built on reactive principles, enabling applications to handle massive concurrency with fewer resources compared to traditional blocking architectures.
- Mono: Represents a single, possibly empty, asynchronous sequence of 0 or 1 element.
- Flux: Represents a sequence of 0 to N elements.
Aspect-Oriented Programming (AOP)
AOP is a programming paradigm that allows for the separation of concerns by enabling the addition of behavior to existing code without modifying it. Spring AOP is typically used for cross-cutting concerns like logging, security, and transaction management.
- Aspect: The modularization of a concern that cuts across multiple classes.
- Advice: The action taken by an aspect at a specific join point is known as advice. Types include Before, After, AfterReturning, AfterThrowing, and Around.
- Join Point: A specific point in the execution of a program, such as when a method is called or an exception is handled.
- Pointcut: The pointcut expression defines the predicate used to match join points. Advice is linked to this pointcut expression and executes at each join point that the pointcut expression matches.
Combining WebFlux and AOP
By leveraging AOP in a WebFlux application, we can intercept method calls and trigger asynchronous REST requests. This approach is useful in scenarios where actions such as logging or auditing require data from external services.
Spring AOP Annotations:
@Aspect: Declares the class as an aspect.@Pointcut: Declares the pointcut expression.@Before: Advice that runs before the matched method's execution.@AfterReturning: Advice that runs after the matched method successfully returns.@AfterThrowing: Advice that runs if the matched method throws an exception.@Around: Advice that runs before and after the matched method's execution.
Integrating Spring WebFlux with AOP for Asynchronous REST Calls
External API Implementation
Step 1: Create the New Spring Boot Project
Create a new spring boot project using intelliJ idea and choose the below options:
- Name: external-API
- Language: Java
- Type: Maven
- Packaging: jar
Click on the Next button.
Step 2: Add the Dependencies
Add the following dependencies to the project and click the "Create" button. Refer to the provided image for better understanding.
Once the project is created, the file structure will look like the provided image.
Step 3: Create the ExternalApiController Class
Create a simple endpoint to expose the external API of the project.
Navigate to src > main > java > com.gfg.externalapi and create ExternalApiController with the following code:
Java
package com.gfg.externalapi;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Controller for handling external API requests.
*/
@RestController
public class ExternalApiController {
/**
* Endpoint to fetch data.
*
* @return DataResponse containing sample data.
*/
@GetMapping("/data")
public DataResponse getData() {
return new DataResponse("Sample data from external API");
}
/**
* DataResponse class to encapsulate the response message.
*/
public static class DataResponse {
private String message;
/**
* Constructor to initialize the DataResponse with a message.
*
* @param message the response message.
*/
public DataResponse(String message) {
this.message = message;
}
/**
* Getter for the message.
*
* @return the response message.
*/
public String getMessage() {
return message;
}
/**
* Setter for the message.
*
* @param message the response message to set.
*/
public void setMessage(String message) {
this.message = message;
}
}
}
Step 4: Main Class
No changes are required in the main class. The code for the main class is:
Java
package com.gfg.externalapi;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ExternalApiApplication {
public static void main(String[] args) {
SpringApplication.run(ExternalApiApplication.class, args);
}
}
pom.xml
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.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.gfg</groupId>
<artifactId>external-API</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>external-API</name>
<description>external-API</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-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</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>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
Step 5: Run the Application
Once completed the application, it will run and start at port 8080.
Spring AOP Project Implementation
Step 1: Create the new Spring Reactive Project
We can create the new Spring Reactive project using the IntelliJ Idea. Choose the below options:
- Name: weblux-aop-demo
- Language: Java
- Type: Maven
- packaging: jar
Click on the Next button
Step 2: Add the Dependencies
We can add the below dependencies into the project.Refer the below image for better understanding.
Once create the project then the file structure looks like the below image.
Step 3: Create the ExternalService Class
We can create the simple service class named as ExternalService class
Go to src > main > java > com.gfg.webfluxaopdemo > ExternalService and put the below code.
Java
package com.gfg.webfluxaopdemo;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
@Service
public class ExternalService {
private final WebClient webClient;
public ExternalService(WebClient.Builder webClientBuilder) {
this.webClient = webClientBuilder.baseUrl("http://localhost:8080").build();
}
public Mono<String> getExternalData() {
return webClient.get()
.uri("/data")
.retrieve()
.bodyToMono(DataResponse.class)
.map(DataResponse::getMessage);
}
private static class DataResponse {
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
}
Step 4: Create the LoggingAspect Class
Go to src > main > java > com.gfg.webfluxaopdemo > LoggingAspect and put the below code.
Java
package com.gfg.webfluxaopdemo;
import com.gfg.webfluxaopdemo.ExternalService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
@Aspect
@Component
@Slf4j
@RequiredArgsConstructor
public class LoggingAspect {
private final ExternalService externalService;
@Pointcut("execution(* com.gfg.webfluxaopdemo.*.*(..))")
public void controllerMethods() {}
@Pointcut("execution(* com.gfg.webfluxaopdemo.ExternalService.*(..))")
public void externalServiceMethods() {}
@AfterReturning(pointcut = "controllerMethods() && !externalServiceMethods()", returning = "result")
public void logAfterMethodReturn(Object result) {
Mono<String> response = externalService.getExternalData();
response.subscribe(data -> log.info("Data from external service: {}", data));
}
}
Step 5: Create the DemoController Class
Go to src > main > java > com.gfg.webfluxaopdemo > DemoController and put the below code.
Java
package com.gfg.webfluxaopdemo;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@RestController
public class DemoController {
@GetMapping("/hello")
public Mono<String> sayHello() {
return Mono.just("Hello, WebFlux with AOP!");
}
}
Step 6: Main Class
No changes are required in the main class
Java
package com.gfg.webfluxaopdemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class WebfluxAopDemoApplication {
public static void main(String[] args) {
SpringApplication.run(WebfluxAopDemoApplication.class, args);
}
}
pom.xml
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.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.gfg</groupId>
<artifactId>webflux-aop-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>webflux-aop-demo</name>
<description>webflux-aop-demo</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-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</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 7: Run the application
Once the project is complete, it will run and start on port 8081.
Step 8: Testing the Application
Test the endpoints using Postman:
GET http://localhost:8081/hello
Output:
Logs Result
Refer to the provided image for the logs result.
Conclusion
In this article, we explored how to set up a Spring WebFlux project with AOP to trigger REST calls during method interception and handle these calls using Mono and Flux. This approach provides a powerful and flexible way to integrate cross-cutting concerns with reactive programming.
Explore
Java Enterprise Edition
Multithreading
Concurrency
JDBC (Java Database Connectivity)
Java Frameworks
JUnit