目前,Java 连接 Redis 分为两种客户端 一种是 Jedis,另一种则是 Lettuce
注:Spring Boot 2.0+ 使用 spring-boot-starter-data-redis 依赖,默认的客户端使用的是 Lettuce
1、Jedis 和 Lettuce 的区别
Jedis 在实现上,是直接连接的 Redis 服务器,在多个线程间共享一个 Jedis 实例是线程不安全的。如果是需要在多线程的场景下使用 Jedis ,需要使用连接池,让每个线程都使用自己的 Jedis 实例,当线程数量增多时,会消耗比较多的物理资源~
与 Jedis 相比的话,Lettuce 完全克服了前者线程不安全的缺点,多个线程可以共享一个实例,而不用担心多线程的并发问题~
2、Spring Boot 集成 Redis
1、添加相关依赖
<!-- 集成redis依赖(默认使用lettuce) --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <!--通过排除 lettuce 依赖来实现 跳过它去用 Jedis,如果需要使用 Jedis,请解除注释下方配置 --> <!-- <exclusions>--> <!-- <exclusion>--> <!-- <groupId>io.lettuce</groupId>--> <!-- <artifactId>lettuce-core</artifactId>--> <!-- </exclusion>--> <!-- </exclusions>--> </dependency> <!-- 连接池所需的依赖 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> <!--引入Jedis 依赖--> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency>
2、添加数据源配置
我新建了一个 application-redis-test.yml 不清楚如何引用外部配置的,请移步 【Spring Boot学习笔记】配置文件的理解
#这个是Redis的配置文件 spring: data: redis: #Redis服务器地址 host: 127.0.0.1 #Redis服务端口 port: 6379 #Redis链接密码 password: 114514 #使用第几个数据库 database: 0 #链接超时时间 timeout: 1000 #这个是针对 lettuce客户端的配置 lettuce: pool: #最大活跃连接 max-active: 8 #最大空闲连接(连接空闲时会保留在连接池中,后续可以直接复用) max-idle: 8 #最小空闲连接 min-idle: 0 max-wait: 1000 # 连接等待时间,单位毫秒 #这个是针对 jedis客户端的配置 jedis: pool: #最大活跃连接 max-active: 8 #最大空闲连接(连接空闲时会保留在连接池中,后续可以直接复用) max-idle: 8 #最小空闲连接 min-idle: 0 max-wait: 1000 # 连接等待时间,单位毫秒
3、定义Redis Config
这个是设定序列化与反序列化格式的~
/** * 这个类是设定key-value的序列化与反序列化 * 告诉 Redis,我要把key序列化为String ,value序列化为Json */ @Configuration public class RedisConfig { @Bean(name="redisTemplate") @SuppressWarnings("all")//暂时解决版本问题,导致报错 无法自动装配。找不到 'RedisConnectionFactory' 类型的 Bean。 public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); // 设置key序列化方式string,RedisSerializer.string() 等价于 new StringRedisSerializer() redisTemplate.setKeySerializer(RedisSerializer.string()); // 设置value的序列化方式json,使用GenericJackson2JsonRedisSerializer替换默认序列化,RedisSerializer.json() 等价于 new GenericJackson2JsonRedisSerializer() redisTemplate.setValueSerializer(RedisSerializer.json()); // 设置hash的key的序列化方式 redisTemplate.setHashKeySerializer(RedisSerializer.string()); // 设置hash的value的序列化方式 redisTemplate.setHashValueSerializer(RedisSerializer.json()); // 使配置生效 redisTemplate.afterPropertiesSet(); return redisTemplate; } }
4、创建 RedisUtil
这个根据自己需要,创建一个Redis统一的操作类,方便后续开发,这边我直接就用构造方法把Template注入了~
不到万不得已,别去用手动获取Bean!!!
@Component//非常重要,不然无法使用构造方法注入!,这个是告诉 Spring ,要以它管理的Bean加载到容器中~ public class RedisUtil { //private static RedisTemplate<String, Object> redisTemplate = SpringContextUtils.getBean("redisTemplate", RedisTemplate.class); private final RedisTemplate<String, Object> _redisTemplate; public RedisUtil(RedisTemplate<String, Object> redisTemplate) { _redisTemplate = redisTemplate; } /** * 指定缓存失效时间 * * @param key 键 * @param time 时间(秒) * @return */ public Boolean expire(String key, long time) { try { if (time > 0) { _redisTemplate.expire(key, time, TimeUnit.SECONDS); } return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 根据key 获取过期时间 * * @param key 键 不能为null * @return 时间(秒) 返回-1代表为永久有效 失效时间为0,说明该主键未设置失效时间(失效时间默认为-1) */ public Long getExpire(String key) { return _redisTemplate.getExpire(key, TimeUnit.SECONDS); } /** * 判断key是否存在 * * @param key 键 * @return true 存在 false 不存在 */ public Boolean hasKey(String key) { try { return _redisTemplate.hasKey(key); } catch (Exception e) { e.printStackTrace(); return false; } } /** * 删除缓存 * * @param key 可以传一个值 或多个 */ @SuppressWarnings("unchecked") public void del(String... key) { if (key != null && key.length > 0) { if (key.length == 1) { _redisTemplate.delete(key[0]); } else { var keys = (List<String>) CollectionUtils.arrayToList(key); _redisTemplate.delete(keys); } } } /** * 普通缓存获取 * * @param key 键 * @return 值 */ @SuppressWarnings("unchecked") public <T> T get(String key) { T res=key==null?null:(T)(_redisTemplate.opsForValue().get(key)); //return key == null ? null : (T) _redisTemplate.opsForValue().get(key); return res; } /** * 普通缓存放入并设置时间 * * @param key 键 * @param value 值 * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期 * @return true成功 false 失败 */ public boolean set(String key, Object value, long time) { try { if (time > 0) { _redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); } else { _redisTemplate.opsForValue().set(key, value); } return true; } catch (Exception e) { e.printStackTrace(); return false; } } }
目前,前期的部署操作已经完成,下面创建测试方法~
5、初步体验
在Service里面创建一个方法,可以根据自己的需求进行编写,我这边是如果在Redis中查到了这个用户的话,我把这个人的uid改为20000,如果没有查到的话,正常从数据库获取一遍,然后写入Redis,Key就是 KEY_USERINFO_xxx,这个 xxx 就是查询的用户名,大概像这样 (仅仅一个测试案例,别杠!)
private final RedisUtil _redisUtil; public UserService(RedisUtil redisUtil) { _redisUtil=redisUtil; } //缓存实验 public UserInfo redisTest(String name){ UserInfo ui=null; ui=_redisUtil.get("KEY_USERINFO_"+name);//获取数据 if(ui==null) { QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>(); queryWrapper.lambda().like(UserInfo::getName, name).last("LIMIT 1"); ui = userInfoMapper.selectOne(queryWrapper); if (ui != null) { _redisUtil.set("KEY_USERINFO_" + name, ui, (60 * 60 * 24));//缓存一天 } }else{ ui.setUid(20000); } return ui; }
Controller那边我就不写了,删除就是 _redisUtil.del(key),当然多个key一起传也行~
测试效果:
刷新一下就变成了
完事~

微信扫码查看本文
发表评论