Caching is a crucial method for increasing application performance since it stores frequently requested data in a temporary manner. Caching speeds up response times, lessens backend strain, and enhances user experience by cutting down on the number of costly activities like database searches and API requests.
1. Spring Boot Cache Annotations
Spring Boot provides a set of annotations to simplify caching:
- @Cacheable: Caches the result of a method based on its parameters.
- @CachePut: Updates the cache with the method’s result.
- @CacheEvict: Removes data from the cache.
- @Caching: Allows combining multiple caching annotations using a single method.
2. Enabling Caching in Spring Boot
To enable caching in your Spring Boot application, add the @EnableCaching annotation to one of your configuration classes. This annotation triggers a post-processor that inspects each Spring bean for caching annotations.
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableCaching
public class CacheConfig {
}
3. Using In-Memory Caching
By default, Spring Boot uses a simple in-memory cache with ConcurrentHashMap. Let's create a sample caching mechanism for a product service:
Step 1: Create a Product Model
public class Product {
private Long id;
private String name;
// Include getters , setters and the constructer here
}
Step 2: Implement a Service with Caching
Here’s how to use @Cacheable to cache method results:
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class ProductService {
@Cacheable(value = "products", key = "#id")
public Product getProductById(Long id) {
// Simulate a slow database query
try {
Thread.sleep(3000); // Simulates a delay
} catch (InterruptedException e) {
e.printStackTrace();
}
return new Product(id, "Sample Product");
}
}
In this example, the getProductById method is annotated with @Cacheable. The first time the method is called with a specific id, the result is stored in the cache. Subsequent calls with the same id retrieve the result from the cache instead of executing the method again.
4. Using External Cache Providers (e.g., Redis, EhCache)
It’s often better to use external cache providers like Redis, EhCache, or Hazelcast for distributed caching for production applications. Let’s see how to use Redis as a cache provider.
Step 1: Add Redis Dependencies
Add the following dependency to your pom.xml file:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
Step 2: Configure Redis
Add Redis configuration to your application.properties file:
spring.data.redis.host=localhost
spring.data.redis.port=6379
Step 3: Create a Redis Cache Manager
Configure the Redis cache manager in a configuration class:
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
@Configuration
@EnableCaching
public class RedisCacheConfig {
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
return RedisCacheManager.builder(redisConnectionFactory).build();
}
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
This configuration sets up Redis as the cache provider for your Spring Boot application.
“I will explain Redis caching in depth in a seperate blog”
5. Conditional Caching with @Cacheable
You can use the condition and unless attributes of @Cacheable to control caching behaviour conditionally.
In the below example, caching occurs only if the id is greater than 10.
@Cacheable(value = "products", key = "#id", condition = "#id > 10")
public Product getProductById(Long id) {
// Method logic
}
6. Updating and Removing Caches with @CachePut and @CacheEvict
- Updating the Cache with @CachePut
@CachePut allows you to update the cache when the method is called:
@CachePut(value = "products", key = "#product.id")
public Product updateProduct(Product product) {
// Update product logic
return product;
}
- Removing Cache Entries with @CacheEvict
@CacheEvict is used to remove entries from the cache. This is useful when you want to invalidate a cache after certain operations (e.g., deleting a product).
@CacheEvict(value = "products", key = "#id")
public void deleteProduct(Long id) {
// Delete product logic
}
You can also clear the entire cache using allEntries = true:
@CacheEvict(value = "products", allEntries = true)
public void clearCache() {
// Logic to clear cache
}
7. Additional Configuration and Best Practices
Cache Expiration: If using an external cache provider, configure cache expiration to avoid stale data. For example, in Redis, you can set TTL (Time-To-Live) for cache entries.
Logging: Enable cache logging for monitoring cache hits and misses.
Avoid Over-Caching: Cache only data that is frequently accessed and expensive to retrieve.
Use Unique Cache Names: Use unique names for caches in multi-module projects to avoid cache collisions.
** Conclusion**
You can significantly enhance the user experience while lowering backend load by following best practices and carefully planning your caching strategy. You can optimize application performance with a flexible toolkit thanks to Spring Boot’s caching capabilities.
Github: https://github.com/tharindu1998/spring-boot-caching-example
References
https://docs.spring.io/spring-boot/reference/io/caching.html
Top comments (1)
Amazing explanation. Thank you for the post.