前言
在使用IDEA创建SpringBoot项目的时候老是初始化失败,所以我改成了阿里的: https://start.aliyun.com/
springBoot官方在 boot2.0 之后就将 jedis 改成了 lettuce
jedis:采用的是直连,多个线程操作的话,是不安全的,想要解决的话,使用 jedis pool连接池
lettuce:采用的是netty,实例可以在多个线程中进行共享,不存在线程不安全的情况,可以减少线程数量
1.创建Boot项目
2.将 Added dependencies 中的都 打上勾勾
3.源码分析
package org.springframework.boot.autoconfigure.data.redis;
@Configuration( proxyBeanMethods = false)
@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties({RedisProperties.class})
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {
public RedisAutoConfiguration() {
}
@Bean
//意思是没有 name 是 redisTemplate Bean的时候才生效
@ConditionalOnMissingBean( name = {"redisTemplate"} )
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
//默认的 RedisTemplate 没有过多的设置,redis对象都是需要序列化!
//两个泛型都是 object,object的类型,我们后使用需要强制转换<string,object>
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
//由于 string 是redis中最常使用的类型,所以说单独提出来了一个bean !
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
package org.springframework.boot.autoconfigure.data.redis;
import java.time.Duration;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties( prefix = "spring.redis" )
public class RedisProperties {
// redis默认有 16个数据库 默认是使用第 0 个
private int database = 0;
// ???
private String url;
// 链接地址 默认为本机
private String host = "localhost";
// 用户密码
private String password;
// 链接端口号
private int port = 6379;
// SSL加密
private boolean ssl;
// 超时时间 单位为 ms
private Duration timeout;
// 用户连接名
private String clientName;
// 哨兵模式
private Sentinel sentinel;
// 集群的配置
private Cluster cluster;
// boot 2.0后 弃用
private final Jedis jedis = new Jedis();
// boot 2.0后 采用
private final Lettuce lettuce = new Lettuce();
}
4.配置Redis
将 application.properties 改成 application.yml
# 应用服务 WEB 访问端口
server:
port: 8080
# 应用名称
spring:
application:
name: bootRedis
# redis 由于boot版本是 2.0 以上所以不需要设置跟pool相关的 最大连接数、最小连接数什么的
redis:
# 云 redis地址 大家都能用 麻烦别删别人的东西
host: 114.115.208.175
# redis默认有 16个数据库 默认是使用第 0 个 我用第 8 个
database: 8
# 云 redis 没有设置密码
password:
# 连接名称
clientName: "一只小狐狸OvO"
# 超时时间 单位为 ms 我这里是3秒
timeout: 3000
5.创建一个实体类
package com.example.bootredis.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Component;
import java.util.UUID;
@Component //组件
@AllArgsConstructor //有参构造
@NoArgsConstructor//无参构造
@Data//get set
public class userDao {
private String userCode = UUID.randomUUID().toString();
private String userName;
private int userAge;
}
6.创建一个存放key的接口
package com.example.bootredis.common;
/**
* 缓存的key 常量
*
* @author hrrm
*/
public interface CacheConstants {
/**
* 用户 key
* */
String SYS_USER_KEY = "sys_user_tokens";
/**
* 用户名 key
* */
String SYS_USER_NAME_KEY = "sys_user_name_tokens";
}
7.测试往Redis中添加数据
@Autowired
private RedisTemplate redisTemplate;
@Test
void Test(){
userDao user = new userDao();
//opsForValue 类似String
redisTemplate.opsForValue().set(CacheConstants.SYS_USER_NAME_KEY+":"+user.getUserCode(),"一只小狐狸");
//获取刚刚set进去的name 一只小狐狸
System.out.println(redisTemplate.opsForValue().get(CacheConstants.SYS_USER_NAME_KEY+":"+user.getUserCode()));
}
测试结果(成功):
8.往redis中添加对象
错误示范:
@Autowired
private RedisTemplate redisTemplate;
@Test
void Test(){
userDao user = new userDao();
user.setUserName("一只小狐狸OvO");
user.setUserAge(3);
redisTemplate.opsForValue().set(CacheConstants.SYS_USER_KEY+":"+user.getUserCode(),user);
System.out.println(redisTemplate.opsForValue().get(CacheConstants.SYS_USER_KEY+":"+user.getUserCode()));
}
报错如下(因为对象没有序列化):
解决方法1:将对象转换成 Json 格式再保存
@Test
void Test(){
userDao user = new userDao();
user.setUserName("一只小狐狸OvO");
user.setUserAge(3);
//将对象装换成JSON格式的字符串
String JsonString = JSONObject.toJSONString(user);
//往Redis添加值
redisTemplate.opsForValue().set(CacheConstants.SYS_USER_KEY+":"+user.getUserCode(),JsonString);
//从Redis中取值
userDao userGet = JSONObject.parseObject(redisTemplate.opsForValue().get(CacheConstants.SYS_USER_KEY+":"+user.getUserCode()).toString() ,userDao.class);
//打印结果
System.out.println(userGet.getUserCode()+"--"+userGet.getUserName());
}
解决方法2:在实体类中实现序列化 再跑错误示范
9.修改序列化方式
**为什么需要序列化: 默认使用的JDK序列化方式,在RDM工具中查看k-v值时会出现“乱码”,不方便查看 **
package com.example.bootredis.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.net.UnknownHostException;
@Configuration
public class redisConfig {
//编写自己的 redisTemplate
@Bean
@ConditionalOnMissingBean( name = {"myRedisTemplate"} )
public RedisTemplate<String, Object> myRedisTemplate(RedisConnectionFactory factory)
throws UnknownHostException {
//为了开发方便 ,一般使用 <String,Object>
RedisTemplate<String, Object> template = new RedisTemplate();
template.setConnectionFactory(factory);
// JSON序列化 :用Json去解析任意的对象 就是说将所有对象都变成一个Json格式的字符串
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
// String序列化
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// String的所有的key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// Hash的所有的key采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value的序列化采用jackson的序列化方式
template.setValueSerializer(jackson2JsonRedisSerializer);
// Hash的value序列化采用jackson的序列化方式
template.setHashValueSerializer(jackson2JsonRedisSerializer);
// 将所有的 Properties Set
template.afterPropertiesSet();
return template;
}
}
注意:所有的包都是建在xxxApplication的同级目录下,不然spring会找不到
10.编写一个Redis的常用工具类
代码纯手敲,有可能眼花缭乱敲错了
redisTemplate .出来之后 和api的指令是一样的,建议读者自己敲一下,C位工程师太容易迷失自我了
redis中文文档:https://www.redis.net.cn/order/
package com.example.bootredis.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@Component
public final class RedisUtils {
@Autowired
@Qualifier("myRedisTemplate")
private RedisTemplate<String,Object> redisTemplate;
/**
* 清除当前数据库的所有缓存
* */
public void removeAll(){
Set<String> keys = redisTemplate.keys("*");
for (String key : keys) {
redisTemplate.delete(key);
}
}
/**
* 指定缓存失效时间
* @param key 键
* @param time 时间(秒)
* */
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 键
* @return 时间(秒) return 0 代表永久有效 return -2 key不存在
* */
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 键
* */
public void delKey(String... key){
if (key != null && key.length > 0){
if(key.length == 1){
redisTemplate.delete(key[0]);
}else {
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}
//==================================String
/**
* String缓存放入
* @param key 键
* @param value 值
* @return true 成功 false 失败
* */
public boolean setString(String key,String value){
try {
redisTemplate.opsForValue().set(key,value);
return true;
}catch (Exception e){
e.printStackTrace();
return false;
}
}
/**
* String缓存获取
* @param key 键
* @return 值
* */
public Object getString(String key){
return key == null ? null: redisTemplate.opsForValue().get(key);
}
/**
* String缓存放入并设置过期时间
* @param key 键
* @param value 值
* @return true 成功 false 失败
* */
public boolean setString(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;
}
}
/**
* 递增
* @param key 键
* @param delta 要递增几
* */
public long incr(String key,long delta){
if(delta > 0){
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key,delta);
}
/**
* 递减
* @param key 键
* @param delta 要减少几
* */
public long decr(String key,long delta){
if(delta > 0){
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().decrement(key,delta);
}
//==================================Map
/**
* 获取 hashkey 中所有的值
* @param key 键
* @return 对应的多个键值
* */
public Map<Object, Object> hmget(String key){
return redisTemplate.opsForHash().entries(key);
}
/**
* 获取 hashkey 中 对应的的值
* @param key 键
* @param item 项
* @return 获取到的值
* */
public Object hget(String key,String item){
return redisTemplate.opsForHash().get(key,item);
}
/**
* 添加多个键值
* @param key
* @param map 添加多个对应的键值
* @return true 成功 false 失败
* */
public boolean hmset(String key,Map<String,Object> map){
try {
redisTemplate.opsForHash().putAll(key,map);
return true;
}catch (Exception e){
e.printStackTrace();
return false;
}
}
/**
* 添加多个键值 并指定缓存失效时间
* @param key
* @param map 添加多个对应的键值
* @param time 时间(秒)
* @return true 成功 false 失败
* */
public boolean hmset(String key,Map<String,Object> map,long time){
try {
redisTemplate.opsForHash().putAll(key,map);
if(time > 0){
expire(key,time);
}
return true;
}catch (Exception e){
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
* @param key 键
* @param item 项
* @param value 添加多个对应的键值
* */
public boolean hset(String key,String item,Object value){
try {
redisTemplate.opsForHash().put(key,item,value);
return true;
}catch (Exception e){
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建 并指定缓存失效时间
* @param key 键
* @param item 项
* @param time 时间(秒)注意:如果已经存在的hash表有时间,这里将会替换为原有时间
* @param value 添加多个对应的键值
* */
public boolean hset(String key,String item,long time,Object value){
try {
redisTemplate.opsForHash().put(key,item,value);
if(time > 0){
expire(key,time);
}
return true;
}catch (Exception e){
e.printStackTrace();
return false;
}
}
/**
* 删除hash表中的值
* @param key 键 不能为null
* @param item 项 可以多个 不能为null
* */
public void hdel(String key,Object item){ redisTemplate.opsForHash().delete(key,item);}
/**
* 判断hash表中是否有该项的值
* @param key 键 不能为null
* @param item 项 不能为null
* */
public boolean hHashKey(String key,String item){
return redisTemplate.opsForHash().hasKey(key,item);
}
/**
* hash 递增 如果不存在,就会创建一个 并把新增后的值返回
* @param key 键 不能为null
* @param item 项 不能为null
* @param by 要加几
* */
public double hincr(String key,String item,double by){
return redisTemplate.opsForHash().increment(key,item,by);
}
/**
* hash 递减
* @param key 键 不能为null
* @param item 项 不能为null
* @param by 要减几
* */
public double hdecr(String key,String item,double by){
return redisTemplate.opsForHash().increment(key,item,-by);
}
//==================================Set
/**
* 根据key获取Set中所有的值
* @param key 键 不能为null
* */
public Set<Object> sGet(String key){
try {
return redisTemplate.opsForSet().members(key);
}catch (Exception e){
e.printStackTrace();
return null;
}
}
/**
* 根据value从一个Set中查询,是否存在
* @param key 键
* @param value 值
* @return true 成功 false 失败
* */
public boolean sHashKey(String key,Object value){
try {
return redisTemplate.opsForSet().isMember(key,value);
}catch (Exception e){
e.printStackTrace();
return false;
}
}
/**
* 将数据存入set缓存中
* @param key 键
* @param values 值 可以是多个
* @return 成功个数
* */
public long sSet(String key,Object... values){
try {
return redisTemplate.opsForSet().add(key,values);
}catch (Exception e){
e.printStackTrace();
return 0;
}
}
/**
* 将数据存入set缓存中 并设置有效时间
* @param key 键
* @param values 值 可以是多个
* @param time 过期时间 单位为m
* @return 成功个数
* */
public long sSet(String key,long time,Object... values){
try {
long count = redisTemplate.opsForSet().add(key,values);
if(time > 0){
expire(key,time);
}
return count;
}catch (Exception e){
e.printStackTrace();
return 0;
}
}
/**
* 获取set缓存的长度
* @param key 键
* @return 长度个数
* */
public long sGetSetSize(String key){
try {
return redisTemplate.opsForSet().size(key);
}catch (Exception e){
e.printStackTrace();
return 0;
}
}
/**
* 移除值为value的
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
* */
public long sRemove(String key,Object... values){
try {
return redisTemplate.opsForSet().remove(key,values);
}catch (Exception e){
e.printStackTrace();
return 0;
}
}
//==================================List
/**
* 获取list中的缓存
* @param key 键
* @param state 开始
* @param end 结束 0 到 -1 代表所有的值
* @return 获取到的内容
* */
public List<Object> lGet(String key, long state, long end){
try {
return redisTemplate.opsForList().range(key,state,end);
}catch (Exception e){
e.printStackTrace();
return null;
}
}
/**
* 获取List缓存的长度
* @param key 键
* @return 长度个数
* */
public long lGetListSize(String key){
try {
return redisTemplate.opsForList().size(key);
}catch (Exception e){
e.printStackTrace();
return 0;
}
}
/**
* 通过索引 获取list中的值
* @param key 键
* @param index 索引 index>=0时候 0是表头 1是第二个元素 依次类推:index<0时 -1为表尾 -2为倒数的第二个元素 以此类推
* @return 长度个数
* */
public Object lGetListSize(String key,long index){
try {
return redisTemplate.opsForList().index(key,index);
}catch (Exception e){
e.printStackTrace();
return null;
}
}
/**
* 将list放入缓存当中
* @param key 键
* @param value 值
* @return true 成功 false 失败
* */
public boolean lSet(String key,Object value){
try {
redisTemplate.opsForList().rightPush(key,value);
return true;
}catch (Exception e){
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存当中的最后一个
* @param key 键
* @param value 值
* @return true 成功 false 失败
* */
public boolean lRset(String key,Object value){
try {
redisTemplate.opsForList().rightPush(key,value);
return true;
}catch (Exception e){
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存当中 并设置过期时间
* @param key 键
* @param value 值
* @param time 时间 秒
* @return true 成功 false 失败
* */
public boolean lSet(String key,Object value,long time){
try {
redisTemplate.opsForList().rightPush(key,value);
if(time > 0){
expire(key,time);
}
return true;
}catch (Exception e){
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存当中 数组
* @param key 键
* @param values 值
* @return true 成功 false 失败
* */
public boolean lSetList(String key,List<Object> values){
try {
redisTemplate.opsForList().rightPushAll(key,values);
return true;
}catch (Exception e){
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存当中 并设置过期时间 数组
* @param key 键
* @param values 值
* @param time 时间 秒
* @return true 成功 false 失败
* */
public boolean lSetList(String key,List<Object> values,long time){
try {
redisTemplate.opsForList().rightPushAll(key,values);
if(time > 0){
expire(key,time);
}
return true;
}catch (Exception e){
e.printStackTrace();
return false;
}
}
/**
* 根据索引修改list中的某条数据
* @param key 键
* @param index 索引
* @param value 值
* @return true 成功 false 失败
* */
public boolean lUpdateIndex(String key,long index,Object value){
try {
redisTemplate.opsForList().set(key,index,value);
return true;
}catch (Exception e){
e.printStackTrace();
return false;
}
}
/**
* 移除n个值为value的
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除个数
* */
public long lRemove(String key,long count,Object value){
try {
long remove = redisTemplate.opsForList().remove(key,count,value);
return remove;
}catch (Exception e){
e.printStackTrace();
return 0;
}
}
}
11.在SpringBootTest当中去测试一下刚写好的工具类
@Autowired
private RedisUtils redisUtils;
@Test
void Test(){
//清除缓存中的全部数据
//redisUtils.removeAll();
//创建当前对象
UserDao user = new UserDao();
//过期时间 秒
long time = 20;
//将数据存放到Hash中
redisUtils.hset(CacheConstants.SYS_USER_KEY+":"+user.getUserCode(),CacheConstants.SYS_USER_NAME_KEY+":"+user.getUserCode(),time,"一只小狐狸OvO");
//获取缓存剩余时间
System.out.println("缓存剩余时间:"+redisUtils.getExpire(CacheConstants.SYS_USER_KEY + ":" + user.getUserCode()));
//查看缓存中的这个key的全部数据
System.out.println("keys:"+redisUtils.hmget(CacheConstants.SYS_USER_KEY + ":" + user.getUserCode()));
//查看缓存中的这个key指定项的数据
System.out.println("name:"+redisUtils.hget(CacheConstants.SYS_USER_KEY + ":" + user.getUserCode(), CacheConstants.SYS_USER_NAME_KEY + ":" + user.getUserCode()));
}
测试成功:
序列化之后的可视客户端:
SpringBoot整合Redis成功!!