springboot集成redis
一、boot2与boot1中redis的区别
Spring Boot 1.0 默认使用的是 Jedis 客户端,2.0 替换成 Lettuce,但如果从 Spring Boot 1.5.X 切换过来,几乎感受不大差异,这是因为 spring-boot-starter-data-redis
为我们隔离了其中的差异性。 Lettuce 是一个可伸缩线程安全的 Redis 客户端,多个线程可以共享同一个 RedisConnection,它利用优秀 netty NIO (IO多路复用)框架来高效地管理多个连接。
二、依赖文件
jar包的依赖如下
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
三、配置文件
以yml为例子,集群和单机的配置二选一:
spring:
#redis依赖
redis:
#集群配置
# cluster:
# # 集群重定向次数
# max-redirects: 3
# nodes: 127.0.0.1:7001,127.0.0.1:7002,127.0.0.1:7003,127.0.0.1:7004,127.0.0.1:7005,127.0.0.1:7006
host: 127.0.0.1
port: 6379
#密码 如果没有则不需要设置,如果设置空,使用jedis连接可能会出现异常
password: 123456
#超时时间
timeout: 20000
lettuce:
pool:
# 连接池最大连接数(使用负值表示没有限制)
max-active: 10000
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: 10000
# 连接池中的最大空闲连接
max-idle: 200
# 连接池中的最小空闲连接
min-idle: 50
四、配置redis的配置
@Configuration
public class RedisConfig {
@Bean
public <K,V> RedisTemplate<K,V> redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate<K,V> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
serializer.setObjectMapper(serializingObjectMapper());
// value值的序列化采用jacksonRedisSerializer
template.setValueSerializer(serializer);
template.setHashValueSerializer(serializer);
// key的序列化采用StringRedisSerializer
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setConnectionFactory(redisConnectionFactory);
return template;
}
/**
* 获取序列化的jackson对象
* @return com.fasterxml.jackson.databind.ObjectMapper
* @author lcy
* @date 2021/6/29 14:12
**/
public ObjectMapper serializingObjectMapper(){
//创建jsr310包下的javaTimeModule对象,其中包含了jdk8以后的时间类型序列化和反序列化配置
JavaTimeModule javaTimeModule = new JavaTimeModule();
//LocalDateTime序列化设置为yyyy-MM-dd HH:mm:ss
LocalDateTimeSerializer localDateTimeSerializer =
new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
//增加序列化到配置当中
javaTimeModule.addSerializer(LocalDateTime.class,localDateTimeSerializer);
//LocalDateTime反序列化设置为yyyy-MM-dd HH:mm:ss
LocalDateTimeDeserializer localDateTimeDeserializer =
new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
//增加反序列化到配置当中
javaTimeModule.addDeserializer(LocalDateTime.class,localDateTimeDeserializer);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
//注册配置--jdk8类型
objectMapper.registerModule(javaTimeModule);
// 去掉各种@JsonSerialize注解的解析
objectMapper.configure(MapperFeature.USE_ANNOTATIONS,false);
// 只针对非空的值进行序列化
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
// 将类型序列化到属性json字符串中
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL,JsonTypeInfo.As.PROPERTY);
objectMapper.setVisibility(PropertyAccessor.ALL,JsonAutoDetect.Visibility.ANY);
return objectMapper;
}
}
五、注入RedisTemplate对象
用@Autowired注入RedisTemplate对象时,如果指定<key,value>会报错,@Autowired默认按照类型装配的。也就是说,想要获取RedisTemplate< String, Object>的Bean,要根据名字装配那么自然想到使用@Resource,它默认按照名字装配。这样启动就不会报错。
- @Autowired注解是按照类型(byType)装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。
- @Resource默认按照ByName自动注入,由J2EE提供,需要导入包javax.annotation.Resource。@Resource有两个重要的属性:name和type,而Spring的@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以,如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不制定name也不制定type属性,这时将通过反射机制使用byName自动注入策略。
六、封装Service
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
/**
* redis工具类
* @Author lcy
* @Date 2021/4/12 18:07
*/
@Component
@SuppressWarnings(value = {"unchecked","rawtypes"})
public class RedisService {
@Autowired
private RedisTemplate redisTemplate;
/**
* 添加、修改缓存
* @param key key
* @param value value
* @return boolean
* @author lcy
* @date 2019/11/1 19:50
**/
public <K,V> boolean set(K key,V value){
redisTemplate.opsForValue().set(key,value);
return true;
}
/**
* 添加、修改缓存并且设置过期时间--原子性操作,但是只有setEX操作,不包含setNX
* @param key key
* @param value value
* @param expire 过期时间
* @param timeUnit 时间单位
* @return boolean
* @author lcy
* @date 2019/11/1 19:50
**/
public <K,V> boolean set(K key,V value,long expire,TimeUnit timeUnit){
redisTemplate.opsForValue().set(key,value,expire,timeUnit);
return true;
}
/**
* 添加、修改缓存并且设置过期时间--时间单位为:秒
* @param key key
* @param value value
* @param expire 过期时间
* @return boolean
* @author lcy
* @date 2019/11/1 19:50
**/
public <K,V> boolean set(K key,V value,long expire){
return set(key,value,expire,TimeUnit.SECONDS);
}
/**
* 添加、修改缓存并且设置过期时间--原子性操作用于分布式加锁操作等。这里包含了sexNX和setEX两个操作
* @param key key
* @param value value
* @param expire 过期时间
* @param timeUnit 时间单位
* @return boolean
* @author lcy
* @date 2019/11/1 19:50
**/
public <K,V> boolean setIfAbsent(K key,V value,long expire,TimeUnit timeUnit){
return Optional.ofNullable(redisTemplate.opsForValue().setIfAbsent(key,value,expire,timeUnit)).orElse(false);
}
/**
* 获取缓存信息
* @param key key
* @return V
* @author lcy
* @date 2019/11/1 19:50
**/
public <K,V> V get(K key){
ValueOperations<K,V> valueOperations = redisTemplate.opsForValue();
return valueOperations.get(key);
}
/**
* 对key添加过期时间
* @param key key
* @param expire 过期时间
* @param timeUnit 时间单位
* @return boolean
* @author lcy
* @date 2019/11/1 19:50
**/
public <K> boolean expire(K key,long expire,TimeUnit timeUnit){
return Optional.ofNullable(redisTemplate.expire(key,expire,timeUnit)).orElse(false);
}
/**
* 获取key的过期时间 默认单位:秒。-2表示不存在,-1表示永久,正数为正常时间。如果获取失败默认返回-2
* @param key key
* @return Long
* @author lcy
* @date 2019/11/1 19:50
**/
public <K> Long getExpire(K key){
return redisTemplate.getExpire(key);
}
/**
* 根据时间单位,获取key的过期时间 获取key的过期时间。 -2表示不存在,-1表示永久,正数为正常时间。如果获取失败默认返回-2
* @param key key
* @return Long
* @author lcy
* @date 2019/11/1 19:50
**/
public <K> Long getExpire(K key,TimeUnit timeUnit){
return redisTemplate.getExpire(key,timeUnit);
}
/**
* 删除缓存信息
* @param key key
* @return boolean
* @author lcy
* @date 2019/11/1 19:50
**/
public <K> boolean delete(K key){
return Optional.ofNullable(redisTemplate.delete(key)).orElse(false);
}
/**
* 将 key 中储存的数字值增一。 如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。
* <p>
* 如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。
* @param key key
* @return Long
* @author lcy
* @date 2019/11/1 19:50
**/
public <K> Long increment(K key){
return redisTemplate.opsForValue().increment(key);
}
/**
* 将 key 中储存的数字值加上增量
* @param key key
* @param increment 增量
* @return Long
* @author lcy
* @date 2019/11/1 19:50
**/
public <K> Long increment(K key,long increment){
return redisTemplate.opsForValue().increment(key,increment);
}
/**
* 将 key 中储存的数字值加上增量
* @param key key
* @param increment 增量
* @return Double
* @author lcy
* @date 2019/11/1 19:50
**/
public <K> Double increment(K key,double increment){
return redisTemplate.opsForValue().increment(key,increment);
}
/**
* 将 key 中储存的数字值减一。 如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。
* <p>
* 如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。
* @param key key
* @return Long
* @author lcy
* @date 2019/11/1 19:50
**/
public <K> Long decrement(K key){
return redisTemplate.opsForValue().decrement(key);
}
/**
* 将 key 中储存的数字值减去指定数值
* @param key key
* @param increment 指定数值-减量
* @return Long
* @author lcy
* @date 2019/11/1 19:50
**/
public <K> Long decrement(K key,long increment){
return redisTemplate.opsForValue().decrement(key,increment);
}
/**
* 设置hash存储,包含redis key对应一个hash桶。默认value为key
* @param key redis key
* @param hashKey hashKey
* @return boolean
* @author lcy
* @date 2021/4/20 12:35
**/
public <K,HK> boolean setForHash(K key,HK hashKey){
redisTemplate.opsForHash().put(key,hashKey,hashKey);
return true;
}
/**
* 设置hash存储,包含redis key对应一个hash桶
* @param key redis key
* @param hashKey hashKey
* @param value value
* @return boolean
* @author lcy
* @date 2021/4/20 12:35
**/
public <K,HK,HV> boolean setForHash(K key,HK hashKey,HV value){
redisTemplate.opsForHash().put(key,hashKey,value);
return true;
}
/**
* 根据redis key和hash key获取值
* @param key redis key
* @param hashKey hashKey
* @return java.lang.Object
* @author lcy
* @date 2021/4/20 12:34
**/
public <K,HK,HV> HV getForHash(K key,HK hashKey){
HashOperations<K,HK,HV> hashOperations = redisTemplate.opsForHash();
return hashOperations.get(key,hashKey);
}
/**
* 根据redis key删除指定hashkey的值,可以为多个
* @param key redis key
* @param hashKey hashKey
* @return java.lang.long
* @author lcy
* @date 2021/4/20 12:34
**/
public <K,HK> long deleteForHash(K key,HK... hashKey){
return redisTemplate.opsForHash().delete(key,hashKey);
}
/**
* 根据redis key获取hash所有值
* @param key key
* @return java.util.Map<java.lang.Object,java.lang.Object>
* @author lcy
* @date 2021/4/20 12:33
**/
public <K,HK,HV> Map<HK,HV> getForHashEntries(K key){
return redisTemplate.opsForHash().entries(key);
}
/**
* 添加数据到set集合
* @param key redis key
* @param value value
* @return java.lang.long
* @author lcy
* @date 2021/4/20 12:34
**/
public <K,V> long setForSet(K key,V... value){
return Optional.ofNullable(redisTemplate.opsForSet().add(key,value)).orElse(0L);
}
/**
* 根据redis key和判断value是否存在set集合中
* @param key redis key
* @param value value
* @return java.lang.boolean
* @author lcy
* @date 2021/4/20 12:34
**/
public <K,V> boolean checkInSet(K key,V value){
return Optional.ofNullable(redisTemplate.opsForSet().isMember(key,value)).orElse(false);
}
/**
* 根据key获取set集合全部内容
* @param key key
* @return java.util.Set<V>
* @author lcy
* @date 2021/5/17 11:55
**/
public <K,V> Set<V> getForSet(K key){
return redisTemplate.opsForSet().members(key);
}
/**
* 根据key获取集合数据条数
* @param key key
* @return long
* @author lcy
* @date 2021/5/17 11:56
**/
public <K> long getSizeForSet(K key){
return Optional.ofNullable(redisTemplate.opsForSet().size(key)).orElse(0L);
}
/**
* 根据redis key删除set集合指定元素,value可以为多个
* @param key key
* @param values values
* @return long
* @author lcy
* @date 2021/4/20 12:33
**/
public <K,V> long removeFromSet(K key,V... values){
return Optional.ofNullable(redisTemplate.opsForSet().remove(key,values)).orElse(0L);
}
/**
* 根据key获取list集合里的大小
* @param key key
* @return java.lang.Long
* @author lcy
* @date 2021/4/21 10:54
**/
public <K> Long getListSize(K key){
return redisTemplate.opsForList().size(key);
}
/**
* 在集合的最左边(前面)插入数据,并且返回当前集合的size,插入失败返回-1
* @param key key
* @param value value
* @return java.lang.Long
* @author lcy
* @date 2021/4/21 11:13
**/
public <K,V> Long leftPush(K key,V value){
return redisTemplate.opsForList().leftPush(key,value);
}
/**
* 在某个值的左边(前面)插入数据,把value值放到集合中指定的pivot的前面,如果pivot存在的话。如果不存在则插入失败,并且返回-1
* @param key key
* @param pivot 参考值
* @param value value
* @return Long 当前集合size
* @author lcy
* @date 2021/4/21 10:59
**/
public <K,V> Long leftPush(K key,V pivot,V value){
return redisTemplate.opsForList().leftPush(key,pivot,value);
}
/**
* 在集合的最左边(前面)插入数据,注意集合的顺序,index在前面的先插入,也就是说如果集合是[1,2,3],插入以后变成[3,2,1]
* <p>
* 返回当前集合的size,-1表示插入失败
* @param key key
* @param values values--集合数据
* @return java.lang.Long
* @author lcy
* @date 2021/4/21 11:13
**/
public <K,V> Long leftPushAll(K key,Collection<V> values){
return redisTemplate.opsForList().leftPushAll(key,values);
}
/**
* 在集合的最右边(后面)插入数据,并且返回当前集合的size,插入失败返回-1
* @param key key
* @param value value
* @return java.lang.Long
* @author lcy
* @date 2021/4/21 11:13
**/
public <K,V> Long rightPush(K key,V value){
return redisTemplate.opsForList().rightPush(key,value);
}
/**
* 在某个值的最右边(后面)插入数据,把value值放到集合中指定的pivot的后面,如果pivot存在的话。如果不存在则插入失败,并且返回-1
* @param key key
* @param pivot 参考值
* @param value value
* @return Long 当前集合size
* @author lcy
* @date 2021/4/21 10:59
**/
public <K,V> Long rightPush(K key,V pivot,V value){
return redisTemplate.opsForList().leftPush(key,pivot,value);
}
/**
* 在集合的最右边(后面)插入数据,按顺序插入
* <p>
* 返回当前集合的size,-1表示插入失败
* @param key key
* @param values values--集合数据
* @return java.lang.Long
* @author lcy
* @date 2021/4/21 11:13
**/
public <K,V> Long rightPushAll(K key,Collection<V> values){
return redisTemplate.opsForList().rightPushAll(key,values);
}
/**
* 从集合的最左边(前面)当中取出元素
* @param key key
* @return V
* @author lcy
* @date 2021/4/21 12:29
**/
public <K,V> V leftPop(K key){
ListOperations<K,V> listOperations = redisTemplate.opsForList();
return listOperations.leftPop(key);
}
/**
* 从集合的最右边(后面)当中取出元素
* @param key key
* @return V
* @author lcy
* @date 2021/4/21 12:29
**/
public <K,V> V rightPop(K key){
ListOperations<K,V> listOperations = redisTemplate.opsForList();
return listOperations.rightPop(key);
}
/**
* 根据key获取集合数据--因为先查找范围才返回数据,是两个操作,不能保证数据完全准确性,如需要保证数据完整需要通过lua脚本
* @param key key
* @return java.util.List<V>
* @author lcy
* @date 2021/4/21 11:28
**/
public <K,V> List<V> getForList(K key){
Long size = getListSize(key);
return size == null || size == 0 ? new ArrayList<>() : getForList(key,0,size);
}
/**
* 根据key,获取list下标范围集合的数据
* @param key key
* @param start 开始下标
* @param end 结束下标
* @return java.util.List<V>
* @author lcy
* @date 2021/4/21 11:29
**/
public <K,V> List<V> getForList(K key,long start,long end){
return redisTemplate.opsForList().range(key,start,end);
}
/**
* 根据key获取指定集合下标数据,注意:如果不存在会返回null
* @param key key
* @param index 下标
* @return V
* @author lcy
* @date 2021/4/21 11:30
**/
public <K,V> V index(K key,long index){
ListOperations<K,V> listOperations = redisTemplate.opsForList();
return listOperations.index(key,index);
}
}