SpringBoot整合Redis笔记(Kotlin)
准备
项目依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
yaml配置
spring:
redis:
port: 6379
host: localhost
password:
StringRedisTemplate
该类在RedisAutoConfiguration已经自动注入IOC:
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
我们直接注入即可
@SpringBootTest
class RedisApplicationTests {
@Autowired
private lateinit var stringRedisTemplate: StringRedisTemplate
@Test
fun listTest() {
}
}
设置值
@SpringBootTest
class RedisApplicationTests {
@Autowired
private lateinit var stringRedisTemplate: StringRedisTemplate
@Test
fun listTest() {
stringRedisTemplate
.opsForValue().set("user", "kt") //相当于原生redis的SET 不过期
stringRedisTemplate
.opsForValue().set("user", "kt", 1, TimeUnit.MINUTES) //相当于原生redis的SET 加上过期时间
stringRedisTemplate
.opsForValue().setIfAbsent("user", "kt") //相当于原生redis的SETNX 不过期
stringRedisTemplate
.opsForValue().setIfAbsent("user", "kt", 1, TimeUnit.MINUTES) //相当于原生redis的SETNX 加上过期时间
stringRedisTemplate
.opsForValue().setIfPresent("user", "kt") //如存在该key,则将k的value更新为kotlin 不过期
stringRedisTemplate
.opsForValue().setIfPresent("user", "kt", 1, TimeUnit.MINUTES) //如存在该key,则将k的value更新为kotlin 加上过期时间
stringRedisTemplate
.opsForValue().getAndSet("user", "kt") //如存在该key,则返回旧的value值,然后更新该旧value更新
stringRedisTemplate
.opsForValue()
.multiSet(mapOf("1" to "2", "2" to "3", "3" to "4")) //同时设置一个或多个 key-value 对
stringRedisTemplate
.opsForValue()
.multiSetIfAbsent(
mapOf("1" to "2",
"2" to "3",
"3" to "4",
"4" to "5")) //用于所有给定 key 都不存在时
//同时设置一个或多个 key-value 对
}
}
获取值
@SpringBootTest
class RedisApplicationTests {
@Autowired
private lateinit var stringRedisTemplate: StringRedisTemplate
@Test
fun listTest() {
stringRedisTemplate.opsForValue().get("1") //获取指定 key 的值(java写法)。
stringRedisTemplate.opsForValue()["1"] //获取指定 key 的值(kotlin写法)。
stringRedisTemplate.opsForValue().multiGet(listOf("1", "2")) //获取指定的集合里的值,返回的集合的value顺序跟传入集合的key对应
stringRedisTemplate.opsForValue().get("1", 0, 3) //用于获取存储在指定 key 中字符串的子字符串。字符串的截取范围由
//start 和 end 两个偏移量决定(包括 start 和 end 在内)
//例子:若当前key为“1”的value为“kotlin”,get("1", 0, 3)返回值为“kotl”
}
}
骚操作
@SpringBootTest
class RedisApplicationTests {
@Autowired
private lateinit var stringRedisTemplate: StringRedisTemplate
@Test
fun listTest() {
stringRedisTemplate.opsForValue().size("user") //获取指定key的字符串的长度
stringRedisTemplate.opsForValue().append("1", "kotlin") //用于在给定key的value后追加内容,若key
//①存在 -> 在原本的value后追加给定的值
//②不存在 -> 则效果等同redis的SET
//并返回当前redis内存中value的长度
stringRedisTemplate.opsForValue().decrement("num") //将当前key的value减一操作,并返回当前redis内存中value
//若当前的value不为redis支持的递减操作,则抛出异常
stringRedisTemplate.opsForValue().decrement("num",10) //将当前key的value减去指定数值操作,并返回当前redis内存中value
//若当前的value不为redis支持的递减操作,则抛出异常
stringRedisTemplate.opsForValue().increment("num") //将当前key的value增一操作,并返回当前redis内存中value
//若当前的value不为redis支持的递减操作,则抛出异常
stringRedisTemplate.opsForValue().increment("num",10) //将当前key的value增加指定数值操作,并返回当前redis内存中value
//若当前的value不为redis支持的递减操作,则抛出异常
stringRedisTemplate.randomKey() //从redis中随机选中一名幸运key并返回
stringRedisTemplate.delete("user") //删除指定key对应的key-value键值对,并返回删除结果
}
}
列表
@SpringBootTest
class RedisApplicationTests {
@Autowired
private lateinit var stringRedisTemplate: StringRedisTemplate
@Test
fun listTest() {
stringRedisTemplate
.opsForList().leftPop("users") //从列表的左侧弹出元素
stringRedisTemplate //从列表的左侧弹出元素
.opsForList().leftPop("users", 10, TimeUnit.SECONDS) //若该列表无元素,则最多阻塞该连接指定时间单位
//在指定时间单位若有元素插入,则左弹出,阻塞结束
//在指定时间单位若无元素插入,阻塞结束
stringRedisTemplate //从列表的右侧压入该元素
.opsForList().leftPush("user", "GTX") //并但会当前元素的位置
stringRedisTemplate //如果该key存在,则从列表的右侧压入该元素
.opsForList() //并但会当前元素的位置
.leftPushIfPresent("user", "GTX") //如果该key不存在,则不执行任何操作
stringRedisTemplate
.opsForList() //批量左插入
.leftPushAll("user", "NVIDIA", "AMD", "ARM")
stringRedisTemplate
.opsForList()
.rightPopAndLeftPush("users", "students") //将第一个key列表的最右边元素弹出,插入到第二个key列表的最右侧
stringRedisTemplate //将第一个key列表的最右边元素弹出,插入到第二个key列表的最右侧
.opsForList() //若第一个key列表无元素,阻塞指定时间,在指定时间内有元素后则正常执行操作
.rightPopAndLeftPush("users", "students", 10, TimeUnit.SECONDS)
stringRedisTemplate //在指定列表的指定索引插入指定元素
.opsForList().set("users",0,"kt")
}
}
哈希表
@SpringBootTest
class RedisApplicationTests {
@Autowired
private lateinit var stringRedisTemplate: StringRedisTemplate
@Test
fun listTest() {
//在指定key放置指定的key-value
stringRedisTemplate.opsForHash<String, String>().put("user", "id", "1100")
stringRedisTemplate.opsForHash<String, String>().put("user", "username", "ls6666")
stringRedisTemplate.opsForHash<String, String>().put("user", "password", "123456")
//在指定key放置指定的key-value集合
stringRedisTemplate.opsForHash<String, String>().putAll("user", mapOf("id" to "1000", "username" to "ls"))
//若给定的hashKey不存在则在指定key放置指定的key-value,
stringRedisTemplate.opsForHash<String, String>().putIfAbsent("user", "id", "1008611")
//获取指定key的属性的value
stringRedisTemplate.opsForHash<String, String>()["user", "id"] //kotlin
stringRedisTemplate.opsForHash<String, String>().get("user", "id") //Java
//获取指定key的属性的value的集合
stringRedisTemplate.opsForHash<String, String>().multiGet("user", listOf("id", "username"))
//获取指定key的属性集合
stringRedisTemplate.opsForHash<String, String>().keys("user")
//获取指定key的值集合
stringRedisTemplate.opsForHash<String, String>().values("user")
//获取指定key的属性和值,其实就是返回了Map
stringRedisTemplate.opsForHash<String, String>().entries("user")
//获取指定key的k-v的个数
stringRedisTemplate.opsForHash<String, String>().size("user")
//获取指定key的属性的值的长度
stringRedisTemplate.opsForHash<String, String>().lengthOfValue("user", "id")
//删除指定key
stringRedisTemplate.opsForHash<String, String>().delete("user")
//删除指定key的某个属性的k-v
stringRedisTemplate.opsForHash<String, String>().delete("user", "id")
//对指定的key的属性进行增量操作
stringRedisTemplate.opsForHash<String, String>().increment("user", "id", 10)
}
}
集合
@SpringBootTest
class RedisApplicationTests {
@Resource
private lateinit var stringRedisTemplate: StringRedisTemplate
@Test
fun listTest() {
//往集合添加元素
stringRedisTemplate.opsForSet().add("user", "1", "2", "3", "3")
//求两个集合不同的值:求第一个集合有而第二个集合没有的值!!!
//求两个集合不同的值:求第一个集合有而第二个集合没有的值!!!
//求两个集合不同的值:求第一个集合有而第二个集合没有的值!!!
stringRedisTemplate.opsForSet().difference("type", "user")
//求两个集合不同的值:求第一个集合有而第二个集合没有的值并存储到指定集合
stringRedisTemplate.opsForSet().differenceAndStore("user", "type", "hello")
//随机返回指定集合的指定个元素
stringRedisTemplate.opsForSet().distinctRandomMembers("user", 2)
//求集合之间交集
stringRedisTemplate.opsForSet().intersect("type", "user")
stringRedisTemplate.opsForSet().intersect("type", listOf("set1", "set2"))
//求集合之间交集并存储到指定集合
stringRedisTemplate.opsForSet().intersectAndStore(listOf("type", "user"), "newset")
//该集合是否包含该元素
stringRedisTemplate.opsForSet().isMember("user", "1")
//返回该集合的所有元素
stringRedisTemplate.opsForSet().members("user")
//随机返回返回该集合的一个元素
stringRedisTemplate.opsForSet().randomMember("user")
//随机返回返回该集合的给定个元素
stringRedisTemplate.opsForSet().randomMembers("user", 10)
//将指定集合的某个元素移动至指定的集合
stringRedisTemplate.opsForSet().move("user", "10", "move")
//集合弹出所有元素
stringRedisTemplate.opsForSet().pop("user")
//集合弹出指定数量的元素
stringRedisTemplate.opsForSet().pop("user", 2)
//返回集合的大小
stringRedisTemplate.opsForSet().size("user")
//返回给定集合的并集
stringRedisTemplate.opsForSet().union("user", "type")
stringRedisTemplate.opsForSet().union("user", listOf("set1", "set2"))
//将给定集合的并集并存储到指定集合
stringRedisTemplate.opsForSet().unionAndStore("user", "type", "xxx")
}
}
有序集合
@SpringBootTest
class RedisApplicationTests {
@Resource
private lateinit var stringRedisTemplate: StringRedisTemplate
@Test
fun listTest() {
val hashSet = HashSet<ZSetOperations.TypedTuple<String>>()
for (i in 1..20)
hashSet.add(DefaultTypedTuple("$i", RandomUtil.randomDouble(1.0, 20.0)))
//单个添加元素
stringRedisTemplate.opsForZSet().add("lishuang", "ls", 20.0)
//批量添加元素
stringRedisTemplate.opsForZSet().add("user", hashSet)
//返回指定集合的在score区间的元素的个数
stringRedisTemplate.opsForZSet().count("user", 15.0, 18.0)
//给指定结合的某个元素增量指定的score
stringRedisTemplate.opsForZSet().incrementScore("user", "ls", 5.0)
//返回指定位置区间的元素的集合
stringRedisTemplate.opsForZSet().range("user", 1, 5)
//返回指定score区间的元素的集合
stringRedisTemplate.opsForZSet().rangeByScore("user", 1.0, 5.0)
//删除该集合
stringRedisTemplate.opsForZSet().remove("user")
//删除该集合指定的元素
stringRedisTemplate.opsForZSet().remove("user", "v1", "v2")
//删除集合指定区间的元素
stringRedisTemplate.opsForZSet().removeRange("user", 1, 5)
}
}
常用操作
@SpringBootTest
class RedisApplicationTests {
@Resource
private lateinit var stringRedisTemplate: StringRedisTemplate
@Test
fun listTest() {
//设置过期时间
stringRedisTemplate.expire("user", 1, TimeUnit.MINUTES)
//设置在指定日期过期
stringRedisTemplate.expireAt("user", DateUtil.offsetDay(Date(), 1))
//坚持下去!!!永不过期!!!
stringRedisTemplate.persist("user")
//获取到期时间(秒)
stringRedisTemplate.getExpire("user")
//获取到期时间并格式化指定单位
stringRedisTemplate.getExpire("user", TimeUnit.HOURS)
//判断是否有该key记录
stringRedisTemplate.hasKey("user")
//更新key的名字
stringRedisTemplate.rename("user", "userX")
stringRedisTemplate.renameIfAbsent("user", "userX")
stringRedisTemplate.multi() //开启事务
// ....(操作)....
stringRedisTemplate.exec() //提交事务
//返回该key对应的数据结构
stringRedisTemplate.type("user")
//Redis Watch 命令用于监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断
stringRedisTemplate.watch("user")
stringRedisTemplate.watch(listOf("user1", "user2"))
//取消监听
stringRedisTemplate.unwatch()
}
}
自定义RedisTemplate
序列化实体java类
实体类
data class User(var username:String?,var password:String?,var date: Date?):Serializable{ //不管是java还是kotlin一定要声明空构造器!!!
constructor():this(null,null,null)
}
注入我们自定义的RedisTemplate
@Bean
fun userRedisTemplate(redisConnectionFactory:RedisConnectionFactory): RedisTemplate<String, User> {
val redisTemplate = RedisTemplate<String, User>()
redisTemplate.keySerializer = StringRedisSerializer() //key的序列化器
redisTemplate.valueSerializer = Jackson2JsonRedisSerializer(User::class.java) //value的序列化器
redisTemplate.setConnectionFactory(redisConnectionFactory) //配置好redis的连接
return redisTemplate
}
使用
@SpringBootTest
class RedisApplicationTests {
@Autowired
private lateinit var userRedisTemplate: RedisTemplate<String, User>
@Test
fun listTest() {
redisTemplate.opsForValue().set("user", User("ls", "666666", DateUtil.parseDate("1999-03-14")))
redisTemplate.opsForValue().get("user")
}
}
测试一下,成功,Perfect!
序列化字节数组
实体类
data class User(var username:String?,var password:String?,var date: Date?):Serializable{ //不管是java还是kotlin一定要声明空构造器!!!
constructor():this(null,null,null)
}
注入我们自定义的RedisTemplate
@Bean
fun byteArrayRedisTemplate(redisConnectionFactory: RedisConnectionFactory): RedisTemplate<String, ByteArray> {
val redisTemplate = RedisTemplate<String, ByteArray>()
redisTemplate.keySerializer = StringRedisSerializer()
redisTemplate.valueSerializer = JdkSerializationRedisSerializer()
redisTemplate.setConnectionFactory(redisConnectionFactory)
return redisTemplate
}
对象与字节数组互转工具
class SerializableTool {
companion object {
fun ObjectToByteArray(obj: Any?): ByteArray {
val byteArrayOutputStream = ByteArrayOutputStream()
val objectOutputStream = ObjectOutputStream(byteArrayOutputStream)
objectOutputStream.writeObject(obj)
objectOutputStream.flush()
return byteArrayOutputStream.toByteArray()
}
fun ByteArrayToObject(byteArray: ByteArray?): Any {
val `in` = ByteArrayInputStream(byteArray)
val sIn = ObjectInputStream(`in`)
return sIn.readObject()
}
}
}
使用
@SpringBootTest
class RedisApplicationTests {
@Autowired
private lateinit var byteRedisTemplate: RedisTemplate<String, ByteArray>
@Test
fun listTest() {
byteRedisTemplate.opsForValue().set("user", SerializableTool.ObjectToByteArray(User("ls", "6666", DateUtil.parseDate("1999/03/14"))))
val user = SerializableTool.ByteArrayToObject(byteRedisTemplate.opsForValue().get("user")) as User
println(user) //User(username=ls, password=6666, date=1999-03-14 00:00:00)
}
}
我们可以把任何东西存成数组放在redis里面,然后取出来自己进行序列化,就像我上面的序列化java类的例子,不过我的做法是不好的,好的做法是对于某种序列化我们应自定义RedisTemplate,序列化和反序列化时只要注入该RedisTemplate即可,不用考虑我上述的自定义序列化(SerializableTool)工具的使用。
Redis订阅-发布
原生写法
订阅端
语法:SUBSCRIBE channel [channel …]
连接上redis-cli,我们订阅一个名为kitty的信道:
SUBSCRIBE kitty
当前redis-cli客户端会在前台一直等待发布者发布消息,一旦有发布者在kitty这个信道上发布消息,这个订阅端就会收到消息显示在命令行上。
发布端
语法:PUBLISH channel message
连接上redis-cli,我们在kitty的信道上发布一个信息:
PUBLISH kitty "hello redis!"
改命令会在kitty信道上发布一个消息“hello redis!”并返回收到该消息的订阅者的数量,订阅kitty的都会在第一时间收到该消息,很像UDP的特性
整合Springboot
消息封装
data class User(var username:String?,var password:String?,var date: Date?):Serializable{
constructor():this(null,null,null)
}
自定义RedisTemplate
@Bean
fun userRedisTemplate(redisConnectionFactory: RedisConnectionFactory): RedisTemplate<String, User> {
val redisTemplate = RedisTemplate<String, User>()
redisTemplate.keySerializer = StringRedisSerializer()
redisTemplate.valueSerializer = Jackson2JsonRedisSerializer(User::class.java)
redisTemplate.setConnectionFactory(redisConnectionFactory)
return redisTemplate
}
消息订阅监听
@Component
class RedisSubscriber : MessageListenerAdapter() {
@Autowired
private lateinit var redisTemplate: RedisTemplate<String, User>
override fun onMessage(message: Message, bytes: ByteArray?) {
//获取二进制消息体
val body = message.body
//获取信道二进制
val channel = message.channel
//反序列化消息体 使用的是jackson2JsonRedisSerializer
val msg = redisTemplate.valueSerializer.deserialize(body)
//反序列化channel名字 因为channel本来就是字符串 所以我们使用字符串序列化器 默认是UTF-8
val topic = redisTemplate.stringSerializer.deserialize(channel)
//因为序列化后是字符串类型,所以就会变成json,我们在通过工具转成Bean
val user = JSONObject(msg).toBean(User::class.java)
println(user)
println("监听到topic为" + topic + "的消息:" + msg)
}
}
将监听者与channel绑定
@Configuration
public class Redis {
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
//该订阅者订阅了信道channel1和channel2
container.addMessageListener(listenerAdapter, new PatternTopic("channel1"));
container.addMessageListener(listenerAdapter, new PatternTopic("channel2"));
return container;
}
}
消息发布
@RestController
public class SampleController {
@Autowired
private RedisTemplate<String, User> redisTemplate;
@GetMapping("/send")
String send() {
redisTemplate.convertAndSend("channel1", new User("lishuang", "jiayou0000", new Date()));
return "success";
}
}