Mastering Spring Data Redis: Installation, Caching, and Advanced Usage
This comprehensive guide walks you through installing Redis on Linux and Windows, configuring Spring Cache annotations, setting up JSON serialization with RedisTemplate, using connection pools, and implementing a flexible RedisService with controller examples, providing all the essential steps to integrate Redis into your Spring Boot applications.
Redis Installation
Spring Data Redis is the Spring framework's way to operate Redis. This article covers Redis installation, using Spring Cache with Redis, connection pool usage, and RedisTemplate operations.
Linux
We use Docker for installation.
Download Redis 5.0 Docker image:
<code>docker pull redis:5.0</code>Start Redis container:
<code>docker run -p 6379:6379 --name redis \
-v /mydata/redis/data:/data \
-d redis:5.0 redis-server --appendonly yes</code>Windows
Use the following steps for Windows.
Download Windows Redis from GitHub .
Extract to a directory.
Run
redis-server.exe redis.windows.conffrom the command line.
Spring Cache with Redis
Spring Cache Overview
When Spring Boot uses Redis as a cache, the simplest way is Spring Cache. By adding annotations such as @Cacheable , @CachePut , @CacheEvict , and @EnableCaching , you can implement caching without dealing with Redis APIs directly.
Common Annotations
@EnableCaching
Enable caching, usually placed on the main application class.
@Cacheable
Retrieves data from cache if present; otherwise executes the method and stores the result. Typical attributes:
value : cache name (required).
key : cache key, supports SpEL.
unless : condition to skip caching.
condition : condition to cache.
@CachePut
Always executes the method and stores the result in cache. Same attributes as
@Cacheable.
@CacheEvict
Removes entries from cache. Typical attributes:
value : cache name.
key : cache key.
condition : condition to evict.
Usage Steps
Add Redis dependency in
pom.xml:
<code><!--redis依赖配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</code>Configure Redis connection in
application.yml:
<code>spring:
redis:
host: 192.168.6.139
database: 0
port: 6379
password: # optional
timeout: 1000ms
</code>Add
@EnableCachingto the main class:
<code>@EnableCaching
@SpringBootApplication
public class MallTinyApplication {
public static void main(String[] args) {
SpringApplication.run(MallTinyApplication.class, args);
}
}
</code>Use cache annotations in service methods, e.g.,
@Cacheableon brand detail retrieval,
@CacheEvicton update/delete.
<code>/**
* PmsBrandService implementation
*/
@Service
public class PmsBrandServiceImpl implements PmsBrandService {
@Autowired
private PmsBrandMapper brandMapper;
@CacheEvict(value = RedisConfig.REDIS_KEY_DATABASE, key = "'pms:brand:'+#id")
public int update(Long id, PmsBrand brand) { ... }
@CacheEvict(value = RedisConfig.REDIS_KEY_DATABASE, key = "'pms:brand:'+#id")
public int delete(Long id) { ... }
@Cacheable(value = RedisConfig.REDIS_KEY_DATABASE, key = "'pms:brand:'+#id", unless = "#result==null")
public PmsBrand getItem(Long id) { ... }
}
</code>After testing, the cached data appeared as garbled characters and had no expiration.
Storing JSON Data
To store standard JSON with expiration, configure RedisTemplate with a JSON serializer and set a cache expiration time.
Create a configuration class (remove
@EnableCachingfrom the main class):
<code>@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
public static final String REDIS_KEY_DATABASE = "mall";
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisSerializer<Object> serializer = redisSerializer();
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(serializer);
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);
template.afterPropertiesSet();
return template;
}
@Bean
public RedisSerializer<Object> redisSerializer() {
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(mapper);
return serializer;
}
@Bean
public RedisCacheManager redisCacheManager(RedisConnectionFactory factory) {
RedisCacheWriter writer = RedisCacheWriter.nonLockingRedisCacheWriter(factory);
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer()))
.entryTtl(Duration.ofDays(1));
return new RedisCacheManager(writer, config);
}
}
</code>After testing, the cached data is now standard JSON with a 1‑day expiration.
Using Redis Connection Pool
Spring Boot 1.5 uses Jedis (non‑thread‑safe), while Spring Boot 2.x defaults to Lettuce (thread‑safe, non‑blocking). Below are steps to configure a Lettuce pool.
Jedis vs Lettuce
Jedis creates a direct connection per thread and is not thread‑safe without a pool.
Lettuce is scalable, thread‑safe, and uses Netty for non‑blocking I/O, allowing multiple threads to share a connection.
Setup Steps
Add Lettuce pool configuration in
application.yml:
<code>spring:
redis:
lettuce:
pool:
max-active: 8 # max connections
max-idle: 8 # max idle
min-idle: 0 # min idle
max-wait: -1ms # no limit
</code>Add
commons-pool2dependency to
pom.xml:
<code><dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
</code>If the dependency is missing, the application will fail with a NoClassDefFoundError .
Free‑Form Redis Operations
Spring Cache is convenient but limited. For custom expiration or intermediate values, use RedisTemplate directly.
RedisService Interface
Defines common operations for strings, hashes, sets, and lists.
<code>public interface RedisService {
void set(String key, Object value, long time);
void set(String key, Object value);
Object get(String key);
Boolean del(String key);
Long del(List<String> keys);
Boolean expire(String key, long time);
Long getExpire(String key);
Boolean hasKey(String key);
Long incr(String key, long delta);
Long decr(String key, long delta);
Object hGet(String key, String hashKey);
Boolean hSet(String key, String hashKey, Object value, long time);
void hSet(String key, String hashKey, Object value);
Map<Object, Object> hGetAll(String key);
Boolean hSetAll(String key, Map<String, Object> map, long time);
void hSetAll(String key, Map<String, Object> map);
void hDel(String key, Object... hashKey);
Boolean hHasKey(String key, String hashKey);
Long hIncr(String key, String hashKey, Long delta);
Long hDecr(String key, String hashKey, Long delta);
Set<Object> sMembers(String key);
Long sAdd(String key, Object... values);
Long sAdd(String key, long time, Object... values);
Boolean sIsMember(String key, Object value);
Long sSize(String key);
Long sRemove(String key, Object... values);
List<Object> lRange(String key, long start, long end);
Long lSize(String key);
Object lIndex(String key, long index);
Long lPush(String key, Object value);
Long lPush(String key, Object value, long time);
Long lPushAll(String key, Object... values);
Long lPushAll(String key, Long time, Object... values);
Long lRemove(String key, long count, Object value);
}
</code>RedisServiceImpl
Implements the interface using RedisTemplate .
<code>@Service
public class RedisServiceImpl implements RedisService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Override
public void set(String key, Object value, long time) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
}
@Override
public void set(String key, Object value) {
redisTemplate.opsForValue().set(key, value);
}
@Override
public Object get(String key) {
return redisTemplate.opsForValue().get(key);
}
@Override
public Boolean del(String key) {
return redisTemplate.delete(key);
}
@Override
public Long del(List<String> keys) {
return redisTemplate.delete(keys);
}
@Override
public Boolean expire(String key, long time) {
return redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
@Override
public Long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
@Override
public Boolean hasKey(String key) {
return redisTemplate.hasKey(key);
}
@Override
public Long incr(String key, long delta) {
return redisTemplate.opsForValue().increment(key, delta);
}
@Override
public Long decr(String key, long delta) {
return redisTemplate.opsForValue().increment(key, -delta);
}
@Override
public Object hGet(String key, String hashKey) {
return redisTemplate.opsForHash().get(key, hashKey);
}
@Override
public Boolean hSet(String key, String hashKey, Object value, long time) {
redisTemplate.opsForHash().put(key, hashKey, value);
return expire(key, time);
}
@Override
public void hSet(String key, String hashKey, Object value) {
redisTemplate.opsForHash().put(key, hashKey, value);
}
@Override
public Map<Object, Object> hGetAll(String key) {
return redisTemplate.opsForHash().entries(key);
}
@Override
public Boolean hSetAll(String key, Map<String, Object> map, long time) {
redisTemplate.opsForHash().putAll(key, map);
return expire(key, time);
}
@Override
public void hSetAll(String key, Map<String, Object> map) {
redisTemplate.opsForHash().putAll(key, map);
}
@Override
public void hDel(String key, Object... hashKey) {
redisTemplate.opsForHash().delete(key, hashKey);
}
@Override
public Boolean hHasKey(String key, String hashKey) {
return redisTemplate.opsForHash().hasKey(key, hashKey);
}
@Override
public Long hIncr(String key, String hashKey, Long delta) {
return redisTemplate.opsForHash().increment(key, hashKey, delta);
}
@Override
public Long hDecr(String key, String hashKey, Long delta) {
return redisTemplate.opsForHash().increment(key, hashKey, -delta);
}
@Override
public Set<Object> sMembers(String key) {
return redisTemplate.opsForSet().members(key);
}
@Override
public Long sAdd(String key, Object... values) {
return redisTemplate.opsForSet().add(key, values);
}
@Override
public Long sAdd(String key, long time, Object... values) {
Long count = redisTemplate.opsForSet().add(key, values);
expire(key, time);
return count;
}
@Override
public Boolean sIsMember(String key, Object value) {
return redisTemplate.opsForSet().isMember(key, value);
}
@Override
public Long sSize(String key) {
return redisTemplate.opsForSet().size(key);
}
@Override
public Long sRemove(String key, Object... values) {
return redisTemplate.opsForSet().remove(key, values);
}
@Override
public List<Object> lRange(String key, long start, long end) {
return redisTemplate.opsForList().range(key, start, end);
}
@Override
public Long lSize(String key) {
return redisTemplate.opsForList().size(key);
}
@Override
public Object lIndex(String key, long index) {
return redisTemplate.opsForList().index(key, index);
}
@Override
public Long lPush(String key, Object value) {
return redisTemplate.opsForList().rightPush(key, value);
}
@Override
public Long lPush(String key, Object value, long time) {
Long idx = redisTemplate.opsForList().rightPush(key, value);
expire(key, time);
return idx;
}
@Override
public Long lPushAll(String key, Object... values) {
return redisTemplate.opsForList().rightPushAll(key, values);
}
@Override
public Long lPushAll(String key, Long time, Object... values) {
Long cnt = redisTemplate.opsForList().rightPushAll(key, values);
expire(key, time);
return cnt;
}
@Override
public Long lRemove(String key, long count, Object value) {
return redisTemplate.opsForList().remove(key, count, value);
}
}
</code>RedisController
Provides endpoints to test the RedisService.
<code>@Api(tags = "RedisController", description = "Redis testing")
@Controller
@RequestMapping("/redis")
public class RedisController {
@Autowired
private RedisService redisService;
@Autowired
private PmsBrandService brandService;
@ApiOperation("Test simple cache")
@RequestMapping(value = "/simpleTest", method = RequestMethod.GET)
@ResponseBody
public CommonResult<PmsBrand> simpleTest() {
List<PmsBrand> list = brandService.list(1, 5);
PmsBrand brand = list.get(0);
String key = "redis:simple:" + brand.getId();
redisService.set(key, brand);
PmsBrand cached = (PmsBrand) redisService.get(key);
return CommonResult.success(cached);
}
@ApiOperation("Test hash cache")
@RequestMapping(value = "/hashTest", method = RequestMethod.GET)
@ResponseBody
public CommonResult<PmsBrand> hashTest() {
List<PmsBrand> list = brandService.list(1, 5);
PmsBrand brand = list.get(0);
String key = "redis:hash:" + brand.getId();
Map<String, Object> map = BeanUtil.beanToMap(brand);
redisService.hSetAll(key, map);
Map<Object, Object> cached = redisService.hGetAll(key);
PmsBrand cachedBrand = BeanUtil.mapToBean(cached, PmsBrand.class, true);
return CommonResult.success(cachedBrand);
}
@ApiOperation("Test set cache")
@RequestMapping(value = "/setTest", method = RequestMethod.GET)
@ResponseBody
public CommonResult<Set<Object>> setTest() {
List<PmsBrand> list = brandService.list(1, 5);
String key = "redis:set:all";
redisService.sAdd(key, (Object[]) ArrayUtil.toArray(list, PmsBrand.class));
redisService.sRemove(key, list.get(0));
Set<Object> cached = redisService.sMembers(key);
return CommonResult.success(cached);
}
@ApiOperation("Test list cache")
@RequestMapping(value = "/listTest", method = RequestMethod.GET)
@ResponseBody
public CommonResult<List<Object>> listTest() {
List<PmsBrand> list = brandService.list(1, 5);
String key = "redis:list:all";
redisService.lPushAll(key, (Object[]) ArrayUtil.toArray(list, PmsBrand.class));
redisService.lRemove(key, 1, list.get(0));
List<Object> cached = redisService.lRange(key, 0, 3);
return CommonResult.success(cached);
}
}
</code>Project Source Code
https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-redis
macrozheng
Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.