Spring Boot Caffeine Caching Example Configuration
Expensive (CPU or I/O) bound operations can slow down your system. Caching is a way to improve the performance of your system. In this tutorial we demonstrate Caffeine Caching using Spring-Boot. You can use caffeine as a caching provider on top of the spring framework caching abstraction.
Maven Dependencies
We use Apache Maven to manage our project dependencies. To start, add the following dependencies to your project. In this example we use com.github.ben-manes.caffeine-caffeine
as the cache provider.
<?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
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.memorynotfound.springboot.caching</groupId>
<artifactId>caffeine</artifactId>
<version>1.0.0-SNAPSHOT</version>
<url>https://memorynotfound.com</url>
<name>Spring Boot - ${project.artifactId}</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.4.RELEASE</version>
</parent>
<dependencies>
<!-- Spring Framework Caching Support -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- caching provider -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Caffeine Caching Service Example
To demonstrate caffeine caching with the spring framework, we wrote a simple service that’ll cache the result based on a condition. Let’s start by defining the interface.
package com.memorynotfound.springboot;
public interface MusicService {
String play(final String instrument);
}
The following service is annotated with the @CacheConfig
annotation and uses two caches directory and instruments. The play
method is annotated with the @Cacheable
annotation given a condition
parameter, meaning that the outcome of this method’ll be cached upon subsequent invocations when the condition
is evaluated to true.
package com.memorynotfound.springboot;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.*;
import org.springframework.stereotype.Service;
@Service
@CacheConfig(cacheNames = {"directory", "instruments"})
public class MusicServiceIml implements MusicService {
private static Logger log = LoggerFactory.getLogger(MusicServiceIml.class);
@Cacheable(condition = "#instrument.equals('trombone')")
public String play(String instrument) {
log.info("Executing: " + this.getClass().getSimpleName() + ".play(\"" + instrument + "\");");
return "paying " + instrument + "!";
}
}
Configure Caffeine Cache
We configure caffeine
by using the application.yml
file. We can create cache directories by appending a comma-separated list to the spring.cache.cache-names
configuration property. We can define custom specifications using the spring.cache.caffeine.spec
configuration property.
CaffeineSpec
supports the following configuration properties:
initialCapacity=[integer]
: Sets the minimum total size for the internal hash tables. Provide a large enough estimate at construction time avoids the need for expensive resizing operation later, but setting this value unnecessarily high wastes memory.maximumSize=[long]
: Specifies the maximum number of entries the cache may contain. Note that the cache may evict an entry before this limit is exceeded or temporarily exceed the threshold while evicting. This feature cannot be used in conjunction withmaximumWeight
.maximumWeight=[long]
: Specifies the maximum weight of entries the cache may contain. Note that the cache may evict an entry before this limit is exceeded or temporarily exceed the threshold while evicting. This feature cannot be used in conjunction withmaximumSize
.expireAfterAccess=[duration]
: Specifies that each entry should be automatically removed from the cache once a fixed duration has elapsed after the entry’s creation.expireAfterWrite=[duration]
: Specifies that each entry should be automatically removed from the cache once a fixed duration has elapsed after the entry’s creation, or the most recent replacement of its value.refreshAfterWrite=[duration]
: Specifies that active entries are eligible for automatic refresh once a fixed duration has elapsed after the entry’s creation, or the most recent replacement of its value.
Durations are represented by an integer, followed by one of “d”, “h”, “m”, or “s”, representing days, hours, minutes, or seconds respectively. There is currently no syntax to request expiration in milliseconds, microseconds, or nanoseconds.
spring:
cache:
cache-names: instruments, directory
caffeine:
spec: maximumSize=500, expireAfterAccess=30s
logging:
pattern:
console: "%-5level - %msg%n"
level:
- error
- com.memorynotfound=trace
The equivalent application.properties
file:
spring.cache.cache-names: instruments, directory
spring.cache.caffeine.spec: maximumSize=500, expireAfterAccess=30s
logging.pattern.console=%-5level - %msg%n
logging.level.com.memorynotfound=trace
logging.level.=error
Testing Caffeine Cache
To demonstrate if our methods are using the cache, we wrote a simple application. The play
method is executed multiple times.
package com.memorynotfound.springboot;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
@EnableCaching
@SpringBootApplication
public class Application implements CommandLineRunner {
private static Logger log = LoggerFactory.getLogger(Application.class);
@Autowired
private MusicService musicService;
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
@Override
public void run(String... args) throws Exception {
log.info("Spring Boot Caffeine Caching Example Configuration");
play("trombone");
play("guitar");
play("trombone");
play("bass");
play("trombone");
}
private void play(String instrument){
log.info("Calling: " + MusicServiceIml.class.getSimpleName() + ".play(\"" + instrument + "\");");
musicService.play(instrument);
}
}
Console Output
The previous application prints the following output to the console. We can clearly see that the MusicServiceIml.play("trombone"); is cached.
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.5.4.RELEASE)
INFO - Spring Boot Caffeine Caching Example Configuration
INFO - Calling: MusicServiceIml.play("trombone");
INFO - Executing: MusicServiceIml.play("trombone");
INFO - Calling: MusicServiceIml.play("guitar");
INFO - Executing: MusicServiceIml.play("guitar");
INFO - Calling: MusicServiceIml.play("trombone");
INFO - Calling: MusicServiceIml.play("bass");
INFO - Executing: MusicServiceIml.play("bass");
INFO - Calling: MusicServiceIml.play("trombone");
Thanks for interesting article. Is there a way to conditionally use a cache based on the value of a config parameter after the service is started.
Nice article. Helped me a lot!!
i need to add other
spring.cache.caffeine.spec
and generate two cachemanager, it is possible with spring configuration?