← Back to the section

Spring Boot works with Redis out of the box — just add the dependency, point it at an address, and pick the tool you need: manual operations through RedisTemplate, automatic caching through @Cacheable, or session storage through Spring Session.

Connecting and the Lettuce client

By default, Spring Data Redis uses the Lettuce client — asynchronous, thread-safe, built on Netty. Add the dependency:

implementation 'org.springframework.boot:spring-boot-starter-data-redis'

Configure it in application.yml:

spring:
  data:
    redis:
      host: localhost
      port: 6379
      # password: secret   # if authentication is enabled
      # database: 0        # logical database number (0 by default)
      lettuce:
        pool:
          max-active: 8
          max-idle: 4

Spring Boot automatically creates a LettuceConnectionFactory and a RedisTemplate<String, Object>. You only need to override the connection bean for non-standard configurations (cluster, Sentinel, TLS).

RedisTemplate and StringRedisTemplate

RedisTemplate is a general-purpose tool for direct operations with Redis. It exposes "operations" grouped by data structure type:

@Service
public class CounterService {

    private final StringRedisTemplate redis;

    public CounterService(StringRedisTemplate redis) {
        this.redis = redis;
    }

    public void increment(String key) {
        redis.opsForValue().increment(key);
        redis.expire(key, Duration.ofHours(1));
    }

    public Long get(String key) {
        String value = redis.opsForValue().get(key);
        return value != null ? Long.parseLong(value) : 0L;
    }
}

StringRedisTemplate is a RedisTemplate<String, String> with string serialization. It fits when both the key and the value are strings.

Operations by structure type:

MethodRedis data type
opsForValue()String (scalar value)
opsForHash()Hash
opsForList()List
opsForSet()Set
opsForZSet()Sorted Set

Gotcha: default serialization

If you use RedisTemplate<String, Object> without configuring it, values are stored via JdkSerializationRedisSerializer. The data in Redis looks like an unreadable byte stream and is tied to specific Java classes — if the class changes, deserialization breaks.

The right approach is to configure JSON serialization:

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);

        ObjectMapper mapper = new ObjectMapper();
        mapper.activateDefaultTyping(
            mapper.getPolymorphicTypeValidator(),
            ObjectMapper.DefaultTyping.NON_FINAL
        );

        Jackson2JsonRedisSerializer<Object> serializer =
            new Jackson2JsonRedisSerializer<>(mapper, Object.class);

        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);
        return template;
    }
}

After this, values in Redis are stored as readable JSON.

Spring Cache on top of Redis

Spring Cache is an abstraction over a cache. You decorate a method with an annotation, and Spring decides for you: go to the database or return the cached result.

Dependency and enabling

implementation 'org.springframework.boot:spring-boot-starter-cache'
@SpringBootApplication
@EnableCaching
public class Application { ... }

@Cacheable, @CacheEvict, @CachePut

@Service
public class ProductService {

    @Cacheable(value = "products", key = "#id")
    public Product findById(Long id) {
        // called only on a cache miss
        return repository.findById(id).orElseThrow();
    }

    @CacheEvict(value = "products", key = "#product.id")
    public void update(Product product) {
        repository.save(product);
        // the cache entry for this key is evicted
    }

    @CachePut(value = "products", key = "#result.id")
    public Product create(Product product) {
        return repository.save(product);
        // the method always runs, the result is written to the cache
    }

    @CacheEvict(value = "products", allEntries = true)
    public void invalidateAll() {
        // clear the entire products cache
    }
}

RedisCacheManager and TTL

By default the cache lives forever. You can set a TTL through RedisCacheManager:

@Configuration
public class CacheConfig {

    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory factory) {
        RedisCacheConfiguration defaults = RedisCacheConfiguration
            .defaultCacheConfig()
            .entryTtl(Duration.ofMinutes(30))
            .serializeValuesWith(
                RedisSerializationContext.SerializationPair
                    .fromSerializer(new GenericJackson2JsonRedisSerializer())
            );

        Map<String, RedisCacheConfiguration> cacheConfigs = Map.of(
            "products", defaults.entryTtl(Duration.ofHours(1)),
            "sessions", defaults.entryTtl(Duration.ofMinutes(5))
        );

        return RedisCacheManager.builder(factory)
            .cacheDefaults(defaults)
            .withInitialCacheConfigurations(cacheConfigs)
            .build();
    }
}

Short formula: RedisCacheConfiguration is the setup for a single cache; RedisCacheManager assembles them together and registers them with Spring.

Sessions with Spring Session

Spring Session moves user HTTP sessions out of the application's memory and into Redis. This gives you two things:

  1. Horizontal scaling — several application instances share a common session store, so the user doesn't lose their session when switching between them.
  2. Surviving restarts — sessions don't disappear when the application restarts.

Dependency:

implementation 'org.springframework.session:spring-session-data-redis'

In application.yml:

spring:
  session:
    store-type: redis
    timeout: 30m
    redis:
      flush-mode: on-save    # write the session only when it changes
      namespace: "myapp:session"

Nothing else needs configuring — Spring Boot automatically creates a RedisIndexedSessionRepository and wires in the SessionRepositoryFilter. Existing code that uses HttpSession works without changes.

What it looks like in Redis

After running the application with @Cacheable and Spring Session, the keys in Redis will look roughly like this:

# Cache via @Cacheable
> KEYS products::*
1) "products::42"
2) "products::17"

> GET "products::42"
"{\"id\":42,\"name\":\"Widget\",\"price\":9.99}"

> TTL "products::42"
(integer) 3542   # seconds until expiry

# Sessions via Spring Session
> KEYS myapp:session:*
1) "myapp:session:sessions:a3f9c..."
2) "myapp:session:sessions:b12ef..."

In short

  • Lettuce is the default client in Spring Boot; thread-safe, configured in application.yml.
  • StringRedisTemplate is for string operations; RedisTemplate<String, Object> is for complex values.
  • Changing serialization is mandatory: replace JdkSerializationRedisSerializer with GenericJackson2JsonRedisSerializer.
  • @Cacheable / @CacheEvict / @CachePut provide declarative caching; @EnableCaching turns the mechanism on.
  • RedisCacheManager configures TTL separately for each cache.
  • Spring Session moves HTTP sessions into Redis — scaling without losing sessions.

Further reading

  • Redis fundamentals — data structures, TTL, persistence
  • Caching patterns — cache-aside, read-through, write-through, and when to use each
  • Operating Redis — monitoring, replication, Sentinel