Caffeine caching on Spring Boot
Hello guys. This is a quick tutorial to get you started with Caffeine caching on Spring Boot
Context and background: I'm developing a REST API that needs to decodes and authenticate users using Auth0 and OAuth2. The issue is that I have to call the /userinfo endpoint for every request I get from the client, given a JWT token, to get the user information. The JWT token itself doesn't have the user details.
The consequent problem is that Auth0 API has limits to prevent high usage, and I started getting 400, leading the back-end either broken or not property authenticated.
To solve this issue, I added Caffeine, a simple but effective cache system for Spring. It works with just a few line of codes. Here's how you can implement on your Spring project:
<!-- First add the dependency on pom.xml -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
Then
/* enable Caching on your Spring Application adding the annotation as follow */
import org.springframework.cache.annotation.EnableCaching;
@EnableCaching
public class MyApplication {
/* rest of the code */
}
/* Then create a CacheConfig class to hold the caching configuration */
package my.package;
import com.github.benmanes.caffeine.cache.Caffeine;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Configuration class for caching using Caffeine. This class sets up a cache manager with specified
* time-to-live and maximum size for the cache.
*/
@Slf4j
@Configuration
@EnableCaching
public class CacheConfig {
@Value("${cache.auth0.ttl-minutes}")
private int ttlMinutes;
@Value("${cache.auth0.max-size:1000}")
private int maxSize;
@Value("${cache.auth0.access-ttl-minutes:10}")
private int accessTtlMinutes;
/**
* Configures a Caffeine cache manager with specified settings.
*
* @return a configured CacheManager instance.
*/
@Bean
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager("userInfoDto");
cacheManager.setCaffeine(caffeineCacheBuilder());
return cacheManager;
}
/**
* Builds a Caffeine cache configuration with size and time-based eviction policies.
*
* @return a Caffeine cache builder instance.
*/
@Bean
public Caffeine<Object, Object> caffeineCacheBuilder() {
return Caffeine.newBuilder()
.maximumSize(maxSize)
.expireAfterWrite(ttlMinutes, TimeUnit.MINUTES)
.expireAfterAccess(accessTtlMinutes, TimeUnit.MINUTES)
.recordStats()
.removalListener(
(key, value, cause) -> {
log.info("Cache entry removed: {}", cause);
});
}
}
And finally:
/* Add the @Cacheable annotation in the method you want to cache */
@Service
public class AuthService {
/* other members and constructor */
@Cacheable(value = "userInfoDto", key = "#token")
public Optional<UserInfoDto> getUserInfo(String token) {
/* rest of the code */
}
}
That's it. Now you should have a fully working caching, simple but effective.
You can have a minimum control over the caching TTL, maximum size, and access TTL in minutes through configurations directly in the application.properties file, here's an example:
cache.auth0.ttl-minutes = 300
cache.auth0.max-size = 1000
cache.auth0.access-ttl-minutes = 300
Thank you for reading. See you in the next article!