DEV Community

Dev Cookies
Dev Cookies

Posted on

πŸš€ Redis as a Cache in Spring Boot β€” Full Code & Tutorial

🧠 Introduction

Caching is one of the most effective ways to improve performance in microservices and monolithic applications. Redis is an ultra-fast, in-memory key-value store that integrates beautifully with Spring Boot via Spring Cache Abstraction.

This tutorial walks you through setting up Redis as a cache in a Spring Boot application with detailed code, configuration, and explanations.


βš™οΈ Technologies Used

  • Spring Boot 3.x
  • Spring Web
  • Spring Cache
  • Spring Data Redis
  • Redis Server (locally or Docker)
  • Java 17+
  • Maven

πŸ“ Project Structure

redis-cache-demo/
β”œβ”€β”€ controller/
β”‚   └── ProductController.java
β”œβ”€β”€ config/
β”‚   └── RedisConfig.java
β”œβ”€β”€ service/
β”‚   └── ProductService.java
β”œβ”€β”€ model/
β”‚   └── Product.java
β”œβ”€β”€ RedisCacheDemoApplication.java
└── resources/
    └── application.properties
Enter fullscreen mode Exit fullscreen mode

πŸ”§ 1. Add Dependencies (pom.xml)

<dependencies>
    <!-- Spring Boot Web Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Spring Boot Cache -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>

    <!-- Redis Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

    <!-- For JSON Serialization in Redis -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>

    <!-- Lombok (Optional) -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <scope>provided</scope>
    </dependency>
</dependencies>
Enter fullscreen mode Exit fullscreen mode

🌐 2. application.properties

# Redis config
spring.cache.type=redis
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.timeout=60000

# Logging for visibility
logging.level.org.springframework.cache=DEBUG
Enter fullscreen mode Exit fullscreen mode

πŸ“ Tip: You can run Redis locally with Docker:

docker run --name redis -p 6379:6379 redis

πŸ’» 3. Main Class with Caching Enabled

@SpringBootApplication
@EnableCaching // πŸ”₯ Enables Spring's cache abstraction
public class RedisCacheDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(RedisCacheDemoApplication.class, args);
    }
}
Enter fullscreen mode Exit fullscreen mode

🧩 4. Model Class: Product.java

package com.example.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Product implements Serializable {
    private Long id;
    private String name;
    private Double price;
}
Enter fullscreen mode Exit fullscreen mode

πŸ’‘ Serializable is required because Redis stores data as binary by default.


πŸ› οΈ 5. Redis Configuration

package com.example.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.serializer.*;

import java.time.Duration;

@Configuration
public class RedisConfig {

    @Bean
    public RedisCacheConfiguration redisCacheConfiguration() {
        return RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofMinutes(5)) // Set TTL for cache
            .disableCachingNullValues()
            .serializeValuesWith(RedisSerializationContext
                .SerializationPair
                .fromSerializer(new GenericJackson2JsonRedisSerializer()));
    }
}
Enter fullscreen mode Exit fullscreen mode

βš™οΈ 6. Service Layer with Caching

package com.example.service;

import com.example.model.Product;
import org.springframework.cache.annotation.*;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service
public class ProductService {

    private final Map<Long, Product> productDb = new HashMap<>();

    public ProductService() {
        // Simulated database
        productDb.put(1L, new Product(1L, "iPhone 14", 89999.0));
        productDb.put(2L, new Product(2L, "MacBook Pro", 229999.0));
    }

    // CACHE READ
    @Cacheable(value = "productCache", key = "#id")
    public Product getProductById(Long id) {
        simulateSlowService(); // Simulate latency
        return productDb.get(id);
    }

    // CACHE WRITE / UPDATE
    @CachePut(value = "productCache", key = "#product.id")
    public Product updateProduct(Product product) {
        productDb.put(product.getId(), product);
        return product;
    }

    // CACHE DELETE
    @CacheEvict(value = "productCache", key = "#id")
    public void deleteProduct(Long id) {
        productDb.remove(id);
    }

    private void simulateSlowService() {
        try {
            Thread.sleep(3000); // Simulates DB latency
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

πŸ“‘ 7. REST Controller

package com.example.controller;

import com.example.model.Product;
import com.example.service.ProductService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/products")
@RequiredArgsConstructor
public class ProductController {

    private final ProductService productService;

    // GET (with cache)
    @GetMapping("/{id}")
    public ResponseEntity<Product> getProduct(@PathVariable Long id) {
        return ResponseEntity.ok(productService.getProductById(id));
    }

    // PUT (update + refresh cache)
    @PutMapping
    public ResponseEntity<Product> updateProduct(@RequestBody Product product) {
        return ResponseEntity.ok(productService.updateProduct(product));
    }

    // DELETE (evict cache)
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteProduct(@PathVariable Long id) {
        productService.deleteProduct(id);
        return ResponseEntity.noContent().build();
    }
}
Enter fullscreen mode Exit fullscreen mode

πŸ§ͺ Example Request Flow

1. First Request (Cache Miss)

curl http://localhost:8080/products/1
Enter fullscreen mode Exit fullscreen mode

βœ… Output (slow): 3s response time
⏳ DB hit, then cached


2. Second Request (Cache Hit)

curl http://localhost:8080/products/1
Enter fullscreen mode Exit fullscreen mode

⚑ Output (fast): <1s
βœ… From Redis cache


3. Update Request (Cache Refresh)

curl -X PUT http://localhost:8080/products \
     -H "Content-Type: application/json" \
     -d '{"id":1,"name":"iPhone 15","price":95000}'
Enter fullscreen mode Exit fullscreen mode

βœ… Redis cache is updated


4. Delete Request (Evict Cache)

curl -X DELETE http://localhost:8080/products/1
Enter fullscreen mode Exit fullscreen mode

βœ… Cache for id=1 is evicted


πŸ“Œ Redis Insight View (Optional GUI)

  • Install RedisInsight
  • Connect to Redis
  • View keys under productCache::1, etc.

🧠 Best Practices

  • Use meaningful cache names and keys
  • Apply TTL (Time-to-live) to avoid stale data
  • Avoid caching nulls or sensitive data
  • Use cache eviction/update smartly with business logic
  • Monitor with tools like RedisInsight or Spring Boot Actuator

🏁 Conclusion

Using Redis as a cache in Spring Boot is a must-have performance booster for modern apps. With Spring's @Cacheable abstraction, it's easier than ever to implement caching with minimal effort.

By understanding how to configure TTL, handle serialization, and update cache entries, you can build robust and performant backend services.

Top comments (0)