DEV Community

Cover image for Spring Boot Caching: A Comprehensive Guide to Improve Application Performance
Tharindu Dulshan Fernando
Tharindu Dulshan Fernando

Posted on

Spring Boot Caching: A Comprehensive Guide to Improve Application Performance

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 {
}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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");
    }
}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

Step 2: Configure Redis

Add Redis configuration to your application.properties file:

spring.data.redis.host=localhost
spring.data.redis.port=6379
Enter fullscreen mode Exit fullscreen mode

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;
    }
}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode
  • 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
}
Enter fullscreen mode Exit fullscreen mode

You can also clear the entire cache using allEntries = true:

@CacheEvict(value = "products", allEntries = true)
public void clearCache() {
    // Logic to clear cache
}
Enter fullscreen mode Exit fullscreen mode

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

https://www.baeldung.com/spring-cache-tutorial

https://www.javatpoint.com/spring-boot-caching

Top comments (1)

Collapse
 
davitanaka profile image
Davi Tanaka

Amazing explanation. Thank you for the post.