Skip to main content

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

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

Comments

Popular posts from this blog

Deploying Spring Boot microservices on Kubernetes Cluster

This article guides you through the deployment of two Spring Boot microservices, namely "order-service" and "inventory-service," on Kubernetes using "MiniKube" . We will establish communication between them, with "order-service" making calls to an endpoint in "inventory-service." Additionally, we will configure "order-service" to be accessible from the local machine's browser . 1) Create Spring Boot microservices The Spring Boot microservices, "order-service" and "inventory-service," have been developed and can be found in this GitHub repository. If you are interested in learning more about creating Spring Boot REST microservices, please refer to this or this (Reactive) link. 2) Build Docker Images The Docker images for both "order-service" and "inventory-service" have already been generated and deployed on DockerHub, as shown below. codeburps/order-service cod...

Circuit Breaker Pattern with Resilience4J in a Spring Boot Application

Read Also: Spring Cloud Circuit Breaker + Resilience4j Resilience4j is a lightweight fault tolerance library that draws inspiration from Netflix Hystrix but is specifically crafted for functional programming. The library offers higher-order functions, known as decorators , designed to augment any functional interface, lambda expression, or method reference with features such as Circuit Breaker, Rate Limiter, Retry, or Bulkhead . These functionalities can be seamlessly integrated within a project, class, or even applied to a single method. It's possible to layer multiple decorators on any functional interface, lambda expression, or method reference, allowing for versatile and customizable fault tolerance. While numerous annotation-based implementations exist online, this article focuses solely on the reactive approach using router predicates and router functions . How Circuit Breaker Pattern works? In general, a circuit breaker functions as an automatic electrical s...

How to create a basic Spring 6 project using Maven

Below is a step-by-step guide to creating a basic Spring project using Maven. 1) Create a Maven Project Use the following Maven command to create a new Maven project. mvn archetype:generate -DgroupId=com.tb -DartifactId=spring-demo -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false 2) Import in IntelliJ IDEA If you haven't already, open IntelliJ IDEA on your system. Go to "File" > "New" > "Project from Existing Sources..." . In the file dialog, navigate to the directory where your Maven project is located. Select the pom.xml file within the project directory and click "Open." 3) Update pom.xml In total, the application requires the below-mentioned dependencies: 4) Create Spring Configuration Create a Java configuration class that uses annotations to define your Spring beans and their dependencies. This class should be annotated with @Configuration . 5) Create the Main Application C...