How to Use Caching in WebFlux - Caching Mono and Flux
1. Introduction
In traditional projects, using a caching framework to cache a method's return value is a simple task. There are multiple caching framework options available, such as EHcache, Caffeine, jetcache, Guava Cache, and many more.
However, when I actually applied WebFlux to a real project, I discovered that due to asynchronous scheduling in reactive programming, almost all method returns are wrapped in Mono<T> or Flux<T>, making it seem like previous caching frameworks couldn't be easily integrated into the project.
For this reason, I collected information about common caching frameworks' support for project-reactor (the reactive programming framework in WebFlux) and found it wasn't as simple as I thought.
As shown below:
| Framework | Support Status | Related Links |
|---|---|---|
| ehcache | Not supported | Possibility to provide asynchronous or reactive cache in future versions |
| jetcache | Not supported | Does jetcache support spring webflux? |
| reactor-extra | Latest version has stopped updating | reactor-addons |
| caffeine | Supported | Reactive types support for @Cacheable methods but requires Spring 6.1M4 or later |
2. Method 1: Using Caffeine + Spring @Cacheable
This approach has the best integration and is backed by the Spring team, so performance and security should be guaranteed. Unfortunately, it requires Spring 6.1 M4 or later.

Usage:
@SpringBootApplication
@Configuration
@EnableCaching
public class MakeSomeToolsApplication {
public static void main(String[] args) {
SpringApplication.run(MakeSomeToolsApplication.class, args);
}
@Bean
public CaffeineCacheManager caffeineCache() {
CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();
Caffeine<Object, Object> caffeine = Caffeine.newBuilder()
.expireAfterWrite(60, TimeUnit.MINUTES);
caffeineCacheManager.setCaffeine(caffeine);
caffeineCacheManager.setAsyncCacheMode(true);
return caffeineCacheManager;
}
}
- Add the
@EnableCachingannotation - Inject the CaffeineCacheManager bean, and you must set
setAsyncCacheModeto true - this is required - Caffeine.newBuilder() allows you to configure your caching strategy
Then you can use the @Cacheable annotation on methods - authentic and smooth. The downside is it's not customizable enough. If you've used @Cacheable before, you know it can't set expiration times for individual method return values - you need to write your own code to extend this.
3. Method 2: Using AOP Implementation
This doesn't mean writing your own AOP to intercept method execution and set caching strategies. Someone has already done this for us - we just need to use it.
Repository: https://github.com/shaikezr/async-cacheable

Copy these classes into your project, and you can use @AsyncCacheable on methods.

When the project starts, it will automatically set the corresponding caching strategy based on your configuration. However, currently supported features are limited to these three:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AsyncCacheable {
String name() default "default1000Item5MinuteCache";
long maximumSize() default 1000L;
long expireAfterWriteSeconds() default 300L;
}
The good news is that extending it isn't difficult. As long as you know some AOP basics, you can extend it yourself in AsyncCacheableMethodProcessor. So any features Caffeine supports can be added to the annotation.

Some related content about this project, if you want to know more:
Integration Support with Spring Webflux?
4. There's More!
If you're not satisfied with methods 1 and 2, there's another solution - completely customizable and fully controllable. But it requires advanced coding skills.
The author of Caffeine cache wrote an example in the project, attempting to teach us how to perfectly combine project-reactor with Caffeine's AsyncCacheLoader. Looking at it, you need to be very proficient with project-reactor and have a good understanding of Caffeine's async cache to write it well.
Link: https://github.com/ben-manes/caffeine/tree/master/examples/coalescing-bulkloader-reactor