Integrating Redis as a Cache in Spring Boot
To use Redis as a caching layer in Spring Boot, include the spring-boot-starter-cache and spring-boot-starter-data-redis dependencies. Spring Boot auto-configures a RedisTemplate and, when caching is enabled, a RedisCacheManager.
Required Dependencies
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Basic Configuration (application.properties)
spring.redis.host=192.168.66.128
spring.redis.port=6380
spring.cache.cache-names=user-cache
Enable caching by adding @EnableCaching to the main application class:
@SpringBootApplication
@EnableCaching
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Spring Boot automatically configures a RedisCacheManager using default settings. For custom behavior—such as TTL or serialization—define your own CacheManager bean.
Using Cache Annotations
@CacheConfig: Class-level annotation to set a default cache name.@Cacheable: Caches method return values. The key can be cusotmized using SpEL.@Cacheable(cacheNames = "user-cache", key = "#userId") public User fetchUser(Long userId) { return userRepository.findById(userId); }@CachePut: Updates the cache after method execution (e.g., after updating an entity).@CachePut(cacheNames = "user-cache", key = "#user.id") public User updateUser(User user) { return userRepository.save(user); }@CacheEvict: Removes entries from the cache (e.g., on deletion).@CacheEvict(cacheNames = "user-cache", key = "#id") public void removeUser(Long id) { userRepository.deleteById(id); }
Executing Lua Scripts in Redis via Spring Boot
Lua scripts enable atomic, multi-command operations directly on the Redis server, reducing round trips and ensuring consistency.
Example Lua Script (sum.lua)
local x = tonumber(ARGV[1])
local y = tonumber(ARGV[2])
return x + y
Executing from Java
@Service
public class MathService {
private final StringRedisTemplate redisTemplate;
private final ResourceLoader resourceLoader;
public MathService(StringRedisTemplate redisTemplate, ResourceLoader resourceLoader) {
this.redisTemplate = redisTemplate;
this.resourceLoader = resourceLoader;
}
public Long executeSumScript(Long a, Long b) {
RedisScript<Long> script = RedisScript.of(
"local x = tonumber(ARGV[1]); local y = tonumber(ARGV[2]); return x + y",
Long.class
);
return redisTemplate.execute(script, Collections.emptyList(), a, b);
}
public Long executeFromFile(Long a, Long b) throws IOException {
Resource scriptResource = resourceLoader.getResource("classpath:sum.lua");
String scriptText = new String(scriptResource.getInputStream().readAllBytes());
RedisScript<Long> script = RedisScript.of(scriptText, Long.class);
return redisTemplate.execute(script, Collections.emptyList(), a, b);
}
}
Common Use Cases for Lua in Redis
- Atomic counters:
INCRBYwithin a script ensures thread safety. - Codnitional updates: Update a value only if it meets certain criteria.
- Distributed locks: Use
SET key value NX PX timeoutatomically. - Batch data processing: Aggregate or transform multiple keys without client-side loops.
Custom Redis Configuration for Caching
For proper object serialization and TTL control:
@Configuration
@EnableCaching
public class RedisCacheConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(mapper);
template.setValueSerializer(serializer);
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);
template.afterPropertiesSet();
return template;
}
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(10))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new Jackson2JsonRedisSerializer<>(Object.class)))
.disableCachingNullValues();
return RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
}
}
Enabling Redis-Based Session Sharing
To share HTTP sessions across multiple instances (e.g., in a microservices architecture), use Spring Session with Redis.
Add Dependency
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
Enable Redis Session
@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 2592000) // 30 days
public class SessionConfig {
// Optional: customize RedisTemplate if needed
}
Test Session Sharing
Create a controller to store and retrieve session attributes:
@RestController
@RequestMapping("/api/session")
public class SessionController {
@GetMapping("/store")
public String storeUrl(HttpServletRequest request) {
request.getSession().setAttribute("url", request.getRequestURL().toString());
return "Stored";
}
@GetMapping("/info")
public Map<String, Object> getSessionInfo(HttpServletRequest request) {
return Map.of(
"id", request.getSession().getId(),
"url", request.getSession().getAttribute("url")
);
}
}
Run two instances on different ports (e.g., 8080 and 9090). After storing a value on port 8080, retrieving it from port 9090 returns the same session ID and data—confirming session sharing via Redis.