Home Post Spring Framework

Caching in Spring Boot (@Cacheable, @CacheEvict & @CachePut)

Mar 31, 2024

Caching is an important aspect of application performance optimization, and Spring provides caching support through its caching abstraction.

The caching abstraction allows us to cache the results of method calls and retrieve them from the cache instead of executing the method again. This can significantly improve the performance of frequently accessed or computationally expensive methods.

1) Dependencies

For caching support in Spring Boot, we typically need the spring-boot-starter-cache dependency, which includes the required caching libraries.

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

2) Enable Caching

We need to explicitly enable caching by adding the @EnableCaching annotation to one of the configuration classes.

3) Cache Manager

Spring Boot provides default cache managers based on the dependencies on the classpath, such as ConcurrentMapCacheManager or EhCacheCacheManager. We can also customize the cache manager to use other providers like bRedis or Hazelcast.

The following are the cache provider supported by Spring Boot framework: JCache, EhCache, Hazelcast, Infinispan, Couchbase, Redis, Caffeine and Simple.

For example, if "JCache" is present on the classpath. The spring-boot-starter-cache provides the JCacheCacheManager.

Similarly, the RedisCacheManager is automatically configured when we configure Redis. The default configuration is set by using property spring.cache.redis.*.

If we want to use Redis then we need to add the following dependency:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-data-redis</artifactId>
</dependency>

If no matching dependency is found in the classpath, it creates an in-memory cache (ConcurrentMapCacheManager) using one concurrent HashMap.

4) Annotations

In Spring Boot, the annotations @Cacheable, @CacheEvict, and @CachePut are part of the Spring Framework's caching abstraction and provide declarative caching support.

4.1) @Cacheable annotation

Once caching is enabled, @Cacheable annotation can be applied to the method whose response is to be cached.

When a method is invoked, Spring will first check the cache to see if the result is already available. If so, it returns the cached result without executing the method. If not, it executes the method, caches the result, and returns it.

In the above example, the getEmployeeById method is annotated with @Cacheable("employee-cache"), which instructs Spring to cache the method's return value in the "employee-cache" cache.

This annotation has the following attributes:

1) The "value" or "cacheNames" attributes can also be used to provide a cache name (instead of passing it directly).

2) The "key" attribute is the key with which object will be cached. It uniquely identifies each entry in the cache. If we do not specify the key then Spring uses the default mechanism to create the key.

3) The "keyGenerator" attribute is used to define our own key generation mechanism (using custom key generator class).

4) The "cacheManager" attribute is used to specify the name of a cache manager. It is useful for passing our own cache manager when we don't want to use Spring's default one.

5) The "condition" and "unless" attributes allows the object to to cached only if it matches certain condition.

4.2) @CacheEvict annotation

Spring provides several annotations to control cache eviction, i.e., removing entries from the cache. The @CacheEvict annotation can be used to remove specific cache entries when a method is called.

We can remove a single entry from the cache based on the "key" attribute. It also provides an attribute called allEntries=true (which evicts all entries).

4.3) @CachePut annotation

In addition to caching the return value of a method, you can also use the @CachePut annotation to update the cache with the method result. This annotation ensures that the method is always executed, and its result is stored in the cache.

The @CachePut and @Cacheable annotations cannot be used together. @Cacheable annotation skips the method execution while the @CachePut annotation runs the method and put its result in the cache.

4.4) @Caching annotation

Java does not allow multiple annotations of same type to be declared on a given method.

The @Caching annotation allows us to apply multiple caching annotations (such as @Cacheable, @CachePut, and @CacheEvict) to a single method.

4.5) @CacheConfig

The @CacheConfig annotation is used to specify the common caching configuration for a specific class. It allows you to define default cache settings for methods within that class, reducing redundancy and providing a centralized configuration.

5) Example

In this section, we will see how to use "Caching (@Cacheable, @CacheEvict, and @CachePut annotations)" in a "Spring Boot" project.

5.1) Dependencies

The final "pom.xml" should look something like this:

5.2) Main class

The "@SpringBootApplication" annotation is the entry point for our Spring Boot application.

5.3) Configuration

By adding the @EnableCaching annotation to a configuration class or the main application class, you activate the caching infrastructure provided by the Spring Framework.

The @EnableJpaAuditing annotation is used to enable JPA entity auditing in a Spring Boot application.

5.4) Model

We are caching date for "employees", here is the model to represent it:

5.5) Repository

This is where "Spring Data JPA" comes into the picture, we are creating an "EmployeeRepository" implementing "JpaRepository", this will save us from writing any boilerplate code for JDBC stuff.

5.6) Service

It's generally recommended to implement caching in the service layer. The service layer typically deals with individual business use cases or units of work.

By implementing caching in the service layer, you have finer-grained control over which methods or operations are cached.

5.7) Controller

Start by annotating your controller class with the @RestController annotation. Use the @RequestMapping annotation (or its shortcut annotations like @GetMapping, @PostMapping, etc.) to specify the URL path and the HTTP method that the method should handle.

5.8) Properties file

Lets add required "postgresql", "logs" and "swagger" related properties to "application.properties".

5.9) Testing

Now we are all done with our project setup, remember we have added a dependency for "springdoc-openapi-ui". This will help us in testing our cache implementation with an inbuilt interface.

Run the application and swagger UI for testing APIs can be accessed here: http://localhost:8080/swagger-ui.html

Source code: GitHub

avatar

NK Chauhan

NK Chauhan is a Principal Software Engineer with one of the biggest E Commerce company in the World.

Chauhan has around 12 Yrs of experience with a focus on JVM based technologies and Big Data.

His hobbies include playing Cricket, Video Games and hanging with friends.

Categories
Spring Framework
Microservices
BigData
Core Java
Java Concurrency