Spring Boot Caching
Caching is an optimization technique that stores copies of frequently accessed data in memory or another fast-access storage. In Spring Boot, caching can greatly enhance the performance of an application by reducing redundant calculations and improving response times. Spring Boot provides a comprehensive, flexible caching abstraction that allows you to easily integrate different caching mechanisms into your application.
Here’s an overview of how caching works in Spring Boot, and how to implement it effectively.
1. Enable Caching in Spring Boot
To enable caching in a Spring Boot application, you need to:
- Add the
@EnableCaching
annotation to your main application class or any configuration class. - Configure a cache manager to manage caching operations. Spring Boot supports several caching providers (e.g.,
ConcurrentMap
,EhCache
,Redis
,Caffeine
, etc.).
Example: Enabling Caching
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
@SpringBootApplication
@EnableCaching // Enable caching in Spring Boot
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
2. Caching Annotations
Spring provides several annotations to control the caching behavior:
@Cacheable
: Caches the result of a method.@CachePut
: Updates the cache with the result of a method execution.@CacheEvict
: Removes entries from the cache.@Caching
: Combines multiple cache operations (e.g., evicting and putting) on a method.
2.1 @Cacheable
The @Cacheable
annotation is used to cache the result of a method. It checks whether the result for a method call is already present in the cache. If the result is present, the method is not executed, and the cached value is returned. If the result is not present, the method is executed and the result is cached.
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class ProductService {
@Cacheable("products")
public Product getProductById(Long productId) {
// Simulate a slow database call
simulateSlowService();
return new Product(productId, "Sample Product");
}
private void simulateSlowService() {
try {
Thread.sleep(3000); // Simulate delay
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
In this example:
- If
getProductById(1L)
is called multiple times, the result is cached after the first call and returned from the cache in subsequent calls.
2.2 @CachePut
The @CachePut
annotation is used when you want to update the cache regardless of whether the method was called or not. This is useful when you want to refresh the cache with new values.
import org.springframework.cache.annotation.CachePut;
import org.springframework.stereotype.Service;
@Service
public class ProductService {
@CachePut(value = "products", key = "#product.id")
public Product updateProduct(Product product) {
// Save product to the database
return product; // Simulate the update operation
}
}
Here, every time updateProduct
is called, the cache will be updated with the new value of the product
.
2.3 @CacheEvict
The @CacheEvict
annotation removes an entry from the cache. You can use it to remove items when they are updated or deleted.
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
@Service
public class ProductService {
@CacheEvict(value = "products", key = "#productId")
public void deleteProduct(Long productId) {
// Delete product from the database
}
}
In this example, calling deleteProduct
will remove the cached product with the specified productId
.
2.4 @Caching
The @Caching
annotation allows you to apply multiple caching operations in one place. You can combine @Cacheable
, @CacheEvict
, and @CachePut
.
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
@Service
public class ProductService {
@Caching(
put = @CachePut(value = "products", key = "#product.id"),
evict = @CacheEvict(value = "products", key = "#product.id")
)
public Product saveProduct(Product product) {
// Save the product and evict existing cache
return product;
}
}
3. Configuring a Cache Manager
Spring Boot provides default support for caching using ConcurrentMap
, but you can customize the cache provider by adding relevant dependencies and configuration.
3.1 In-Memory Caching (ConcurrentMap)
Spring Boot can use ConcurrentMapCacheManager
by default, which stores the cache in memory.
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.CacheManager;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("products"); // Cache name is "products"
}
}
3.2 Using Redis for Caching
You can integrate Redis as a distributed caching provider by adding the Redis dependency to your pom.xml
or build.gradle
:
<!-- Add Redis dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
Configure Redis as the cache manager:
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.redis.RedisCacheManager;
import org.springframework.cache.redis.RedisCacheConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
@Configuration
@EnableCaching
public class RedisCacheConfig {
@Bean
public CacheManager cacheManager(RedisTemplate<String, Object> redisTemplate) {
RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig();
return RedisCacheManager.builder(redisTemplate).cacheDefaults(cacheConfig).build();
}
}
3.3 Using Caffeine for Caching
Caffeine is an in-memory caching library that offers higher performance compared to simple ConcurrentMap
cache. To use it, add the following dependency:
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
Then configure it in your application.properties
or application.yml
file.
spring.cache.type=caffeine
spring.cache.caffeine.spec=maximumSize=500,expireAfterAccess=600s
Alternatively, you can configure it manually:
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CaffeineCacheConfig {
@Bean
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder().maximumSize(100).expireAfterAccess(10, TimeUnit.MINUTES));
return cacheManager;
}
}
4. Cache Names and Key Generation
- Cache Names: A cache name is required to identify the cache. It’s specified in annotations like
@Cacheable("cacheName")
. Each cache manager can have multiple caches, each identified by its name. - Cache Key: By default, Spring Boot uses the method parameters as the cache key. You can customize the cache key using the
key
attribute in annotations like@Cacheable(key = "#id")
.
5. Cache Configuration in application.properties
Spring Boot allows you to configure caching properties directly in the application.properties
or application.yml
file:
spring.cache.type=redis # Redis as the cache manager
spring.cache.redis.time-to-live=600000 # Cache expiration time in milliseconds
spring.cache.caffeine.spec=maximumSize=500,expireAfterWrite=10m
Conclusion
Spring Boot’s caching support simplifies the integration of caching in your application. By leveraging annotations like @Cacheable
, @CachePut
, and @CacheEvict
, and selecting the right caching provider (e.g., Redis, Caffeine, EhCache), you can drastically improve the performance and scalability of your applications.
Make sure to choose the cache manager based on your application’s needs, such as using an in-memory cache for small-scale applications or distributed caching systems like Redis for large-scale, multi-instance applications.