Spring Boot Tutorial

Spring Boot - Software Setup and Configuration (STS/Eclipse/IntelliJ)

Prerequisite (Spring Core Concepts)

Spring Boot Core

Spring Boot with REST API

Spring Boot with Database and Data JPA

Spring Boot with Kafka

Spring Boot with AOP

Spring Boot - Caching

Caching in Spring Boot aims to improve the performance of an application by storing the result of expensive computations or frequently accessed data so that subsequent requests can be served faster. Spring's caching abstraction allows you to seamlessly add caching into your application without coupling to any specific cache provider.

Here's how to set up and use caching in a Spring Boot application:

1. Setting Up Caching:

Dependencies:

Include the Spring Boot cache starter dependency in your pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

Enable Caching:

In your main Spring Boot application class or any configuration class, add the @EnableCaching annotation:

@SpringBootApplication
@EnableCaching
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

2. Using Cache Annotations:

With caching set up, you can use various annotations to define caching behavior:

  • @Cacheable: This annotation is used at the method level to indicate that the result of the method should be cached. The first time the method is called, the result will be stored in the cache. Subsequent calls with the same arguments will return the cached value.
@Cacheable(value = "books", key = "#isbn")
public Book getBookByIsbn(String isbn) {
    //... fetch the book from a database or another source
}

In this example, "books" is the cache's name, and the key attribute specifies that the ISBN parameter will be used as the cache key.

  • @CachePut: Indicates that a method's return value should always be stored in the cache. Useful when updating data and the cache.
@CachePut(value = "books", key = "#book.isbn")
public Book updateBook(Book book) {
    //... update the book in a database
    return book;
}
  • @CacheEvict: Used to remove data from the cache. It's particularly useful when deleting data.
@CacheEvict(value = "books", key = "#isbn")
public void deleteBookByIsbn(String isbn) {
    //... delete the book from a database
}
  • @Caching: This is a group annotation that lets you combine multiple cache operations. For instance, when a method affects multiple cache names or involves both putting and evicting.
@Caching(
    put = {@CachePut(value = "book", key = "#book.id")},
    evict = {@CacheEvict(value = "allBooksCache")}
)
public Book saveBook(Book book) {
    //... save logic
    return book;
}

3. Cache Configuration:

By default, Spring Boot uses a simple in-memory cache, but you can easily switch to other cache providers like EhCache, Redis, Hazelcast, etc., by adding the appropriate dependencies and, in some cases, some minimal configuration.

You can also define and configure cache managers in Java code. For example:

@Bean
public CacheManager cacheManager() {
    SimpleCacheManager cacheManager = new SimpleCacheManager();
    cacheManager.setCaches(Arrays.asList(new ConcurrentMapCache("books")));
    return cacheManager;
}

4. Cache Eviction:

It's important to manage the lifecycle of cached data, especially in scenarios where the underlying data changes or the cache can grow indefinitely. Spring provides:

  • @CacheEvict: To remove specific entries or all entries from a cache.
  • Cache policies like TTL (Time To Live) or LRU (Least Recently Used) which can be set based on the cache provider you're using.

5. Conditional Caching:

You can conditionally apply caching using conditions in your caching annotations:

@Cacheable(value = "books", key = "#isbn", condition = "#isbn != null")
public Book getBookByIsbn(String isbn) {
    //... logic
}

In this example, caching is applied only when the ISBN is not null.

Conclusion:

Caching is a powerful feature in Spring Boot that can significantly improve application performance. However, it's crucial to manage cached data properly to ensure data consistency and application stability. Proper cache eviction and expiration strategies should be in place based on the application's specific requirements.

  1. Configuring and enabling caching in Spring Boot:

    • Enable caching in a Spring Boot application using @EnableCaching.
    • Example:
      // Enable caching in a Spring Boot application
      @SpringBootApplication
      @EnableCaching
      public class MyApplication {
          // Application code
      }
      
  2. Using @Cacheable annotation in Spring Boot:

    • Use @Cacheable annotation to cache method results based on specified conditions.
    • Example:
      // Cacheable method in Spring Boot
      @Cacheable("myCache")
      public String getData(String key) {
          // Method logic
      }
      
  3. Cache eviction strategies in Spring Boot:

    • Specify cache eviction policies to remove entries based on conditions.
    • Example:
      // Evict cache entry after 10 minutes
      @CacheEvict(value = "myCache", key = "#key", beforeInvocation = false, allEntries = false, condition = "#key.length() < 10")
      public void evictCache(String key) {
          // Eviction logic
      }
      
  4. Conditional caching in Spring Boot:

    • Conditionally cache results based on specific conditions using @Cacheable.
    • Example:
      // Conditional caching based on a property
      @Cacheable(value = "myCache", condition = "#{T(java.lang.System).getProperty('caching.enabled') == 'true'}")
      public String getData(String key) {
          // Cache data if caching is enabled
      }
      
  5. In-memory caching with Spring Boot:

    • Utilize an in-memory cache provider (e.g., Caffeine) for caching.
    • Example (with pom.xml dependency):
      <!-- Caffeine dependency -->
      <dependency>
          <groupId>com.github.ben-manes.caffeine</groupId>
          <artifactId>caffeine</artifactId>
      </dependency>
      
      Example (configuration):
      // Caffeine configuration in Spring Boot
      @Configuration
      public class CacheConfig {
      
          @Bean
          public CacheManager cacheManager() {
              return new CaffeineCacheManager("myCache");
          }
      }
      
  6. Integrating Ehcache with Spring Boot caching:

    • Integrate Ehcache as a caching provider in Spring Boot.
    • Example (with pom.xml dependency):
      <!-- Ehcache dependency -->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-cache</artifactId>
      </dependency>
      <dependency>
          <groupId>net.sf.ehcache</groupId>
          <artifactId>ehcache</artifactId>
      </dependency>
      
      Example (configuration):
      // Ehcache configuration in Spring Boot
      @Configuration
      public class CacheConfig {
      
          @Bean
          public CacheManager cacheManager() {
              return new EhCacheCacheManager(ehCacheCacheManager().getObject());
          }
      
          @Bean
          public EhCacheManagerFactoryBean ehCacheCacheManager() {
              EhCacheManagerFactoryBean factory = new EhCacheManagerFactoryBean();
              factory.setConfigLocation(new ClassPathResource("ehcache.xml"));
              factory.setShared(true);
              return factory;
          }
      }
      
  7. Redis caching in Spring Boot applications:

    • Use Redis as an external caching provider.
    • Example (with pom.xml dependency):
      <!-- Redis dependency -->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-data-redis</artifactId>
      </dependency>
      
      Example (configuration):
      // Redis configuration in Spring Boot
      @Configuration
      public class CacheConfig {
      
          @Bean
          public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
              return RedisCacheManager.builder(redisConnectionFactory).build();
          }
      }